From e1a987efb68d5d716ec74ee1fa724298fdde83f0 Mon Sep 17 00:00:00 2001 From: vivet Date: Tue, 27 Oct 2026 15:04:35 +0100 Subject: [PATCH 1/2] Updated skills, prompts and AGENTS.md --- .github/workflows/build-and-deploy.yml | 2 +- .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.ApiClients.Audit/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.ApiClients.Entity/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.ApiClients.RootLogIn/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.ApiClients/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Auth.External.Custom/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Auth.External.Microsoft/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Auth.RootLogin/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Authorization/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.ContentNegotiation/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Cookies/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.CustomConfigSection/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.CustomMiddleware/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.CustomService/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Data.Audit/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Data.EntityEvents/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Data.Identity.Auth.ApiKey/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- .../AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Data.Identity.Auth.Jwt/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Data.Identity/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Data.InMemory/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Data.LazyLoading/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Data.MySql.Collation/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Data.MySql.Mappings/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Data.MySql.Spatial/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Data.MySql.StoredProcedures/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Data.MySql.Views/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Data.MySql/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Data.PostgreSQL.Spatial/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Data.PostgreSQL/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Data.Repository.AutoSave/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Data.Repository.Includes/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Data.SoftDelete/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Data.SqLite/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Data.SqlServer.Spatial/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Data.SqlServer/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Data.Triggers/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Documentation.Csp/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Documentation/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.ErrorHandling/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Eventing.RabbitMq/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.HealthChecks/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Hosting.Http/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Hosting.Https/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Hosting.MultipartLimits/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Localization/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Logging.Log4Net/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Logging.Microsoft/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Logging.NLog/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Logging.Serilog/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Metrics/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.MultipartJson/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- .../AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- .../AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.PolicyHeaders.Cors/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.PolicyHeaders.ForwardedHeaders/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.PolicyHeaders.FrameOptions/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.PolicyHeaders.Hsts/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.PolicyHeaders.ReferrerPolicy/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.PolicyHeaders.Robots/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.PolicyHeaders.XssProtection/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.RequestTracing/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.ResponseCache/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.ResponseCompression/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Session/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.StartupTasks/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.StaticFiles/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Storage.Azure/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Storage.Local/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.TimeZone/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.Versioning/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api.VirusScan/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Api._Blank/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Console.CustomConfigSection/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Console.CustomService/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Console.Data.InMemory/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Console.Data.MySql/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Console.Data.PostgreSQL/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Console.Data.SqLite/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Console.Data.SqlServer/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Console.Eventing.RabbitMq/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Console.ExceptionHandling/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Console.Localization/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Console.Logging.Log4Net/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Console.Logging.Microsoft/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Console.Logging.NLog/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Console.Logging.Serilog/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Console.StartupTasks/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Console.Storage.Azure/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Console.Storage.Local/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Console.Workers/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Console._Blank/AGENTS.md | 21 +++++++------------ .../skills/nano-add-data-provider/SKILL.md | 9 ++++++-- .../prompts/nano-add-data-provider.prompt.md | 9 ++++++-- Web._Blank/AGENTS.md | 21 +++++++------------ 277 files changed, 2025 insertions(+), 1565 deletions(-) diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index bc455802..7bc61f19 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -7,7 +7,7 @@ on: branches: - master env: - VERSION: 10.0.10 + VERSION: 10.0.11 jobs: build-and-deploy: runs-on: ubuntu-latest diff --git a/Api.ApiClients.Audit/.claude/skills/nano-add-data-provider/SKILL.md b/Api.ApiClients.Audit/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.ApiClients.Audit/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.ApiClients.Audit/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.ApiClients.Audit/.github/prompts/nano-add-data-provider.prompt.md b/Api.ApiClients.Audit/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.ApiClients.Audit/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.ApiClients.Audit/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.ApiClients.Audit/AGENTS.md b/Api.ApiClients.Audit/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.ApiClients.Audit/AGENTS.md +++ b/Api.ApiClients.Audit/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.ApiClients.Entity/.claude/skills/nano-add-data-provider/SKILL.md b/Api.ApiClients.Entity/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.ApiClients.Entity/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.ApiClients.Entity/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.ApiClients.Entity/.github/prompts/nano-add-data-provider.prompt.md b/Api.ApiClients.Entity/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.ApiClients.Entity/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.ApiClients.Entity/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.ApiClients.Entity/AGENTS.md b/Api.ApiClients.Entity/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.ApiClients.Entity/AGENTS.md +++ b/Api.ApiClients.Entity/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.ApiClients.RootLogIn/.claude/skills/nano-add-data-provider/SKILL.md b/Api.ApiClients.RootLogIn/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.ApiClients.RootLogIn/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.ApiClients.RootLogIn/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.ApiClients.RootLogIn/.github/prompts/nano-add-data-provider.prompt.md b/Api.ApiClients.RootLogIn/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.ApiClients.RootLogIn/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.ApiClients.RootLogIn/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.ApiClients.RootLogIn/AGENTS.md b/Api.ApiClients.RootLogIn/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.ApiClients.RootLogIn/AGENTS.md +++ b/Api.ApiClients.RootLogIn/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.ApiClients/.claude/skills/nano-add-data-provider/SKILL.md b/Api.ApiClients/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.ApiClients/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.ApiClients/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.ApiClients/.github/prompts/nano-add-data-provider.prompt.md b/Api.ApiClients/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.ApiClients/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.ApiClients/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.ApiClients/AGENTS.md b/Api.ApiClients/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.ApiClients/AGENTS.md +++ b/Api.ApiClients/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Auth.External.Custom/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Auth.External.Custom/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Auth.External.Custom/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Auth.External.Custom/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Auth.External.Custom/.github/prompts/nano-add-data-provider.prompt.md b/Api.Auth.External.Custom/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Auth.External.Custom/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Auth.External.Custom/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Auth.External.Custom/AGENTS.md b/Api.Auth.External.Custom/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Auth.External.Custom/AGENTS.md +++ b/Api.Auth.External.Custom/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Auth.External.Microsoft/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Auth.External.Microsoft/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Auth.External.Microsoft/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Auth.External.Microsoft/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Auth.External.Microsoft/.github/prompts/nano-add-data-provider.prompt.md b/Api.Auth.External.Microsoft/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Auth.External.Microsoft/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Auth.External.Microsoft/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Auth.External.Microsoft/AGENTS.md b/Api.Auth.External.Microsoft/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Auth.External.Microsoft/AGENTS.md +++ b/Api.Auth.External.Microsoft/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Auth.RootLogin/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Auth.RootLogin/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Auth.RootLogin/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Auth.RootLogin/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Auth.RootLogin/.github/prompts/nano-add-data-provider.prompt.md b/Api.Auth.RootLogin/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Auth.RootLogin/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Auth.RootLogin/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Auth.RootLogin/AGENTS.md b/Api.Auth.RootLogin/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Auth.RootLogin/AGENTS.md +++ b/Api.Auth.RootLogin/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Authorization/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Authorization/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Authorization/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Authorization/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Authorization/.github/prompts/nano-add-data-provider.prompt.md b/Api.Authorization/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Authorization/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Authorization/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Authorization/AGENTS.md b/Api.Authorization/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Authorization/AGENTS.md +++ b/Api.Authorization/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.ContentNegotiation/.claude/skills/nano-add-data-provider/SKILL.md b/Api.ContentNegotiation/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.ContentNegotiation/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.ContentNegotiation/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.ContentNegotiation/.github/prompts/nano-add-data-provider.prompt.md b/Api.ContentNegotiation/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.ContentNegotiation/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.ContentNegotiation/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.ContentNegotiation/AGENTS.md b/Api.ContentNegotiation/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.ContentNegotiation/AGENTS.md +++ b/Api.ContentNegotiation/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Cookies/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Cookies/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Cookies/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Cookies/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Cookies/.github/prompts/nano-add-data-provider.prompt.md b/Api.Cookies/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Cookies/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Cookies/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Cookies/AGENTS.md b/Api.Cookies/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Cookies/AGENTS.md +++ b/Api.Cookies/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.CustomConfigSection/.claude/skills/nano-add-data-provider/SKILL.md b/Api.CustomConfigSection/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.CustomConfigSection/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.CustomConfigSection/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.CustomConfigSection/.github/prompts/nano-add-data-provider.prompt.md b/Api.CustomConfigSection/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.CustomConfigSection/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.CustomConfigSection/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.CustomConfigSection/AGENTS.md b/Api.CustomConfigSection/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.CustomConfigSection/AGENTS.md +++ b/Api.CustomConfigSection/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.CustomMiddleware/.claude/skills/nano-add-data-provider/SKILL.md b/Api.CustomMiddleware/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.CustomMiddleware/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.CustomMiddleware/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.CustomMiddleware/.github/prompts/nano-add-data-provider.prompt.md b/Api.CustomMiddleware/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.CustomMiddleware/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.CustomMiddleware/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.CustomMiddleware/AGENTS.md b/Api.CustomMiddleware/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.CustomMiddleware/AGENTS.md +++ b/Api.CustomMiddleware/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.CustomService/.claude/skills/nano-add-data-provider/SKILL.md b/Api.CustomService/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.CustomService/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.CustomService/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.CustomService/.github/prompts/nano-add-data-provider.prompt.md b/Api.CustomService/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.CustomService/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.CustomService/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.CustomService/AGENTS.md b/Api.CustomService/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.CustomService/AGENTS.md +++ b/Api.CustomService/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Data.Audit/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Data.Audit/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Data.Audit/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Data.Audit/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.Audit/.github/prompts/nano-add-data-provider.prompt.md b/Api.Data.Audit/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Data.Audit/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Data.Audit/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.Audit/AGENTS.md b/Api.Data.Audit/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Data.Audit/AGENTS.md +++ b/Api.Data.Audit/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Data.EntityEvents/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Data.EntityEvents/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Data.EntityEvents/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Data.EntityEvents/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.EntityEvents/.github/prompts/nano-add-data-provider.prompt.md b/Api.Data.EntityEvents/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Data.EntityEvents/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Data.EntityEvents/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.EntityEvents/AGENTS.md b/Api.Data.EntityEvents/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Data.EntityEvents/AGENTS.md +++ b/Api.Data.EntityEvents/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Data.Identity.Auth.ApiKey/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Data.Identity.Auth.ApiKey/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Data.Identity.Auth.ApiKey/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Data.Identity.Auth.ApiKey/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.Identity.Auth.ApiKey/.github/prompts/nano-add-data-provider.prompt.md b/Api.Data.Identity.Auth.ApiKey/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Data.Identity.Auth.ApiKey/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Data.Identity.Auth.ApiKey/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.Identity.Auth.ApiKey/AGENTS.md b/Api.Data.Identity.Auth.ApiKey/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Data.Identity.Auth.ApiKey/AGENTS.md +++ b/Api.Data.Identity.Auth.ApiKey/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Data.Identity.Auth.External.Custom/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Data.Identity.Auth.External.Custom/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Data.Identity.Auth.External.Custom/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Data.Identity.Auth.External.Custom/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.Identity.Auth.External.Custom/.github/prompts/nano-add-data-provider.prompt.md b/Api.Data.Identity.Auth.External.Custom/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Data.Identity.Auth.External.Custom/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Data.Identity.Auth.External.Custom/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.Identity.Auth.External.Custom/AGENTS.md b/Api.Data.Identity.Auth.External.Custom/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Data.Identity.Auth.External.Custom/AGENTS.md +++ b/Api.Data.Identity.Auth.External.Custom/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Data.Identity.Auth.Jwt/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Data.Identity.Auth.Jwt/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Data.Identity.Auth.Jwt/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Data.Identity.Auth.Jwt/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.Identity.Auth.Jwt/.github/prompts/nano-add-data-provider.prompt.md b/Api.Data.Identity.Auth.Jwt/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Data.Identity.Auth.Jwt/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Data.Identity.Auth.Jwt/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.Identity.Auth.Jwt/AGENTS.md b/Api.Data.Identity.Auth.Jwt/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Data.Identity.Auth.Jwt/AGENTS.md +++ b/Api.Data.Identity.Auth.Jwt/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Data.Identity/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Data.Identity/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Data.Identity/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Data.Identity/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.Identity/.github/prompts/nano-add-data-provider.prompt.md b/Api.Data.Identity/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Data.Identity/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Data.Identity/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.Identity/AGENTS.md b/Api.Data.Identity/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Data.Identity/AGENTS.md +++ b/Api.Data.Identity/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Data.InMemory/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Data.InMemory/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Data.InMemory/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Data.InMemory/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.InMemory/.github/prompts/nano-add-data-provider.prompt.md b/Api.Data.InMemory/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Data.InMemory/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Data.InMemory/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.InMemory/AGENTS.md b/Api.Data.InMemory/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Data.InMemory/AGENTS.md +++ b/Api.Data.InMemory/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Data.LazyLoading/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Data.LazyLoading/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Data.LazyLoading/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Data.LazyLoading/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.LazyLoading/.github/prompts/nano-add-data-provider.prompt.md b/Api.Data.LazyLoading/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Data.LazyLoading/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Data.LazyLoading/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.LazyLoading/AGENTS.md b/Api.Data.LazyLoading/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Data.LazyLoading/AGENTS.md +++ b/Api.Data.LazyLoading/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Data.MySql.Collation/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Data.MySql.Collation/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Data.MySql.Collation/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Data.MySql.Collation/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.MySql.Collation/.github/prompts/nano-add-data-provider.prompt.md b/Api.Data.MySql.Collation/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Data.MySql.Collation/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Data.MySql.Collation/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.MySql.Collation/AGENTS.md b/Api.Data.MySql.Collation/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Data.MySql.Collation/AGENTS.md +++ b/Api.Data.MySql.Collation/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Data.MySql.Mappings/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Data.MySql.Mappings/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Data.MySql.Mappings/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Data.MySql.Mappings/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.MySql.Mappings/.github/prompts/nano-add-data-provider.prompt.md b/Api.Data.MySql.Mappings/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Data.MySql.Mappings/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Data.MySql.Mappings/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.MySql.Mappings/AGENTS.md b/Api.Data.MySql.Mappings/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Data.MySql.Mappings/AGENTS.md +++ b/Api.Data.MySql.Mappings/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Data.MySql.Spatial/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Data.MySql.Spatial/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Data.MySql.Spatial/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Data.MySql.Spatial/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.MySql.Spatial/.github/prompts/nano-add-data-provider.prompt.md b/Api.Data.MySql.Spatial/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Data.MySql.Spatial/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Data.MySql.Spatial/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.MySql.Spatial/AGENTS.md b/Api.Data.MySql.Spatial/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Data.MySql.Spatial/AGENTS.md +++ b/Api.Data.MySql.Spatial/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Data.MySql.StoredProcedures/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Data.MySql.StoredProcedures/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Data.MySql.StoredProcedures/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Data.MySql.StoredProcedures/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.MySql.StoredProcedures/.github/prompts/nano-add-data-provider.prompt.md b/Api.Data.MySql.StoredProcedures/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Data.MySql.StoredProcedures/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Data.MySql.StoredProcedures/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.MySql.StoredProcedures/AGENTS.md b/Api.Data.MySql.StoredProcedures/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Data.MySql.StoredProcedures/AGENTS.md +++ b/Api.Data.MySql.StoredProcedures/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Data.MySql.Views/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Data.MySql.Views/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Data.MySql.Views/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Data.MySql.Views/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.MySql.Views/.github/prompts/nano-add-data-provider.prompt.md b/Api.Data.MySql.Views/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Data.MySql.Views/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Data.MySql.Views/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.MySql.Views/AGENTS.md b/Api.Data.MySql.Views/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Data.MySql.Views/AGENTS.md +++ b/Api.Data.MySql.Views/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Data.MySql/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Data.MySql/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Data.MySql/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Data.MySql/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.MySql/.github/prompts/nano-add-data-provider.prompt.md b/Api.Data.MySql/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Data.MySql/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Data.MySql/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.MySql/AGENTS.md b/Api.Data.MySql/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Data.MySql/AGENTS.md +++ b/Api.Data.MySql/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Data.PostgreSQL.Spatial/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Data.PostgreSQL.Spatial/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Data.PostgreSQL.Spatial/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Data.PostgreSQL.Spatial/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.PostgreSQL.Spatial/.github/prompts/nano-add-data-provider.prompt.md b/Api.Data.PostgreSQL.Spatial/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Data.PostgreSQL.Spatial/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Data.PostgreSQL.Spatial/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.PostgreSQL.Spatial/AGENTS.md b/Api.Data.PostgreSQL.Spatial/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Data.PostgreSQL.Spatial/AGENTS.md +++ b/Api.Data.PostgreSQL.Spatial/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Data.PostgreSQL/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Data.PostgreSQL/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Data.PostgreSQL/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Data.PostgreSQL/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.PostgreSQL/.github/prompts/nano-add-data-provider.prompt.md b/Api.Data.PostgreSQL/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Data.PostgreSQL/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Data.PostgreSQL/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.PostgreSQL/AGENTS.md b/Api.Data.PostgreSQL/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Data.PostgreSQL/AGENTS.md +++ b/Api.Data.PostgreSQL/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Data.Repository.AutoSave/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Data.Repository.AutoSave/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Data.Repository.AutoSave/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Data.Repository.AutoSave/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.Repository.AutoSave/.github/prompts/nano-add-data-provider.prompt.md b/Api.Data.Repository.AutoSave/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Data.Repository.AutoSave/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Data.Repository.AutoSave/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.Repository.AutoSave/AGENTS.md b/Api.Data.Repository.AutoSave/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Data.Repository.AutoSave/AGENTS.md +++ b/Api.Data.Repository.AutoSave/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Data.Repository.Includes/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Data.Repository.Includes/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Data.Repository.Includes/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Data.Repository.Includes/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.Repository.Includes/.github/prompts/nano-add-data-provider.prompt.md b/Api.Data.Repository.Includes/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Data.Repository.Includes/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Data.Repository.Includes/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.Repository.Includes/AGENTS.md b/Api.Data.Repository.Includes/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Data.Repository.Includes/AGENTS.md +++ b/Api.Data.Repository.Includes/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Data.SoftDelete/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Data.SoftDelete/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Data.SoftDelete/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Data.SoftDelete/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.SoftDelete/.github/prompts/nano-add-data-provider.prompt.md b/Api.Data.SoftDelete/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Data.SoftDelete/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Data.SoftDelete/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.SoftDelete/AGENTS.md b/Api.Data.SoftDelete/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Data.SoftDelete/AGENTS.md +++ b/Api.Data.SoftDelete/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Data.SqLite/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Data.SqLite/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Data.SqLite/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Data.SqLite/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.SqLite/.github/prompts/nano-add-data-provider.prompt.md b/Api.Data.SqLite/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Data.SqLite/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Data.SqLite/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.SqLite/AGENTS.md b/Api.Data.SqLite/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Data.SqLite/AGENTS.md +++ b/Api.Data.SqLite/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Data.SqlServer.Spatial/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Data.SqlServer.Spatial/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Data.SqlServer.Spatial/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Data.SqlServer.Spatial/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.SqlServer.Spatial/.github/prompts/nano-add-data-provider.prompt.md b/Api.Data.SqlServer.Spatial/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Data.SqlServer.Spatial/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Data.SqlServer.Spatial/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.SqlServer.Spatial/AGENTS.md b/Api.Data.SqlServer.Spatial/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Data.SqlServer.Spatial/AGENTS.md +++ b/Api.Data.SqlServer.Spatial/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Data.SqlServer/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Data.SqlServer/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Data.SqlServer/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Data.SqlServer/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.SqlServer/.github/prompts/nano-add-data-provider.prompt.md b/Api.Data.SqlServer/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Data.SqlServer/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Data.SqlServer/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.SqlServer/AGENTS.md b/Api.Data.SqlServer/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Data.SqlServer/AGENTS.md +++ b/Api.Data.SqlServer/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Data.Triggers/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Data.Triggers/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Data.Triggers/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Data.Triggers/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.Triggers/.github/prompts/nano-add-data-provider.prompt.md b/Api.Data.Triggers/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Data.Triggers/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Data.Triggers/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Data.Triggers/AGENTS.md b/Api.Data.Triggers/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Data.Triggers/AGENTS.md +++ b/Api.Data.Triggers/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Documentation.Csp/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Documentation.Csp/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Documentation.Csp/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Documentation.Csp/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Documentation.Csp/.github/prompts/nano-add-data-provider.prompt.md b/Api.Documentation.Csp/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Documentation.Csp/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Documentation.Csp/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Documentation.Csp/AGENTS.md b/Api.Documentation.Csp/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Documentation.Csp/AGENTS.md +++ b/Api.Documentation.Csp/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Documentation/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Documentation/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Documentation/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Documentation/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Documentation/.github/prompts/nano-add-data-provider.prompt.md b/Api.Documentation/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Documentation/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Documentation/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Documentation/AGENTS.md b/Api.Documentation/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Documentation/AGENTS.md +++ b/Api.Documentation/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.ErrorHandling/.claude/skills/nano-add-data-provider/SKILL.md b/Api.ErrorHandling/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.ErrorHandling/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.ErrorHandling/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.ErrorHandling/.github/prompts/nano-add-data-provider.prompt.md b/Api.ErrorHandling/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.ErrorHandling/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.ErrorHandling/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.ErrorHandling/AGENTS.md b/Api.ErrorHandling/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.ErrorHandling/AGENTS.md +++ b/Api.ErrorHandling/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Eventing.RabbitMq/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Eventing.RabbitMq/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Eventing.RabbitMq/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Eventing.RabbitMq/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Eventing.RabbitMq/.github/prompts/nano-add-data-provider.prompt.md b/Api.Eventing.RabbitMq/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Eventing.RabbitMq/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Eventing.RabbitMq/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Eventing.RabbitMq/AGENTS.md b/Api.Eventing.RabbitMq/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Eventing.RabbitMq/AGENTS.md +++ b/Api.Eventing.RabbitMq/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.HealthChecks/.claude/skills/nano-add-data-provider/SKILL.md b/Api.HealthChecks/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.HealthChecks/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.HealthChecks/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.HealthChecks/.github/prompts/nano-add-data-provider.prompt.md b/Api.HealthChecks/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.HealthChecks/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.HealthChecks/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.HealthChecks/AGENTS.md b/Api.HealthChecks/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.HealthChecks/AGENTS.md +++ b/Api.HealthChecks/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Hosting.Http/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Hosting.Http/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Hosting.Http/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Hosting.Http/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Hosting.Http/.github/prompts/nano-add-data-provider.prompt.md b/Api.Hosting.Http/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Hosting.Http/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Hosting.Http/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Hosting.Http/AGENTS.md b/Api.Hosting.Http/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Hosting.Http/AGENTS.md +++ b/Api.Hosting.Http/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Hosting.Https/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Hosting.Https/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Hosting.Https/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Hosting.Https/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Hosting.Https/.github/prompts/nano-add-data-provider.prompt.md b/Api.Hosting.Https/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Hosting.Https/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Hosting.Https/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Hosting.Https/AGENTS.md b/Api.Hosting.Https/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Hosting.Https/AGENTS.md +++ b/Api.Hosting.Https/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Hosting.MultipartLimits/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Hosting.MultipartLimits/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Hosting.MultipartLimits/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Hosting.MultipartLimits/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Hosting.MultipartLimits/.github/prompts/nano-add-data-provider.prompt.md b/Api.Hosting.MultipartLimits/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Hosting.MultipartLimits/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Hosting.MultipartLimits/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Hosting.MultipartLimits/AGENTS.md b/Api.Hosting.MultipartLimits/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Hosting.MultipartLimits/AGENTS.md +++ b/Api.Hosting.MultipartLimits/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Localization/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Localization/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Localization/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Localization/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Localization/.github/prompts/nano-add-data-provider.prompt.md b/Api.Localization/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Localization/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Localization/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Localization/AGENTS.md b/Api.Localization/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Localization/AGENTS.md +++ b/Api.Localization/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Logging.Log4Net/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Logging.Log4Net/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Logging.Log4Net/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Logging.Log4Net/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Logging.Log4Net/.github/prompts/nano-add-data-provider.prompt.md b/Api.Logging.Log4Net/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Logging.Log4Net/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Logging.Log4Net/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Logging.Log4Net/AGENTS.md b/Api.Logging.Log4Net/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Logging.Log4Net/AGENTS.md +++ b/Api.Logging.Log4Net/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Logging.Microsoft/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Logging.Microsoft/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Logging.Microsoft/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Logging.Microsoft/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Logging.Microsoft/.github/prompts/nano-add-data-provider.prompt.md b/Api.Logging.Microsoft/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Logging.Microsoft/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Logging.Microsoft/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Logging.Microsoft/AGENTS.md b/Api.Logging.Microsoft/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Logging.Microsoft/AGENTS.md +++ b/Api.Logging.Microsoft/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Logging.NLog/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Logging.NLog/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Logging.NLog/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Logging.NLog/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Logging.NLog/.github/prompts/nano-add-data-provider.prompt.md b/Api.Logging.NLog/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Logging.NLog/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Logging.NLog/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Logging.NLog/AGENTS.md b/Api.Logging.NLog/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Logging.NLog/AGENTS.md +++ b/Api.Logging.NLog/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Logging.Serilog/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Logging.Serilog/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Logging.Serilog/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Logging.Serilog/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Logging.Serilog/.github/prompts/nano-add-data-provider.prompt.md b/Api.Logging.Serilog/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Logging.Serilog/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Logging.Serilog/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Logging.Serilog/AGENTS.md b/Api.Logging.Serilog/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Logging.Serilog/AGENTS.md +++ b/Api.Logging.Serilog/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Metrics/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Metrics/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Metrics/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Metrics/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Metrics/.github/prompts/nano-add-data-provider.prompt.md b/Api.Metrics/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Metrics/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Metrics/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Metrics/AGENTS.md b/Api.Metrics/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Metrics/AGENTS.md +++ b/Api.Metrics/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.MultipartJson/.claude/skills/nano-add-data-provider/SKILL.md b/Api.MultipartJson/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.MultipartJson/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.MultipartJson/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.MultipartJson/.github/prompts/nano-add-data-provider.prompt.md b/Api.MultipartJson/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.MultipartJson/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.MultipartJson/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.MultipartJson/AGENTS.md b/Api.MultipartJson/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.MultipartJson/AGENTS.md +++ b/Api.MultipartJson/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.claude/skills/nano-add-data-provider/SKILL.md b/Api.PolicyHeaders.ContentSecurityPolicy/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.PolicyHeaders.ContentSecurityPolicy/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.PolicyHeaders.ContentSecurityPolicy/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.github/prompts/nano-add-data-provider.prompt.md b/Api.PolicyHeaders.ContentSecurityPolicy/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.PolicyHeaders.ContentSecurityPolicy/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.PolicyHeaders.ContentSecurityPolicy/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/AGENTS.md b/Api.PolicyHeaders.ContentSecurityPolicy/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.PolicyHeaders.ContentSecurityPolicy/AGENTS.md +++ b/Api.PolicyHeaders.ContentSecurityPolicy/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.PolicyHeaders.ContentTypeOptions/.claude/skills/nano-add-data-provider/SKILL.md b/Api.PolicyHeaders.ContentTypeOptions/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.PolicyHeaders.ContentTypeOptions/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.PolicyHeaders.ContentTypeOptions/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.PolicyHeaders.ContentTypeOptions/.github/prompts/nano-add-data-provider.prompt.md b/Api.PolicyHeaders.ContentTypeOptions/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.PolicyHeaders.ContentTypeOptions/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.PolicyHeaders.ContentTypeOptions/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.PolicyHeaders.ContentTypeOptions/AGENTS.md b/Api.PolicyHeaders.ContentTypeOptions/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.PolicyHeaders.ContentTypeOptions/AGENTS.md +++ b/Api.PolicyHeaders.ContentTypeOptions/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.PolicyHeaders.Cors/.claude/skills/nano-add-data-provider/SKILL.md b/Api.PolicyHeaders.Cors/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.PolicyHeaders.Cors/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.PolicyHeaders.Cors/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.PolicyHeaders.Cors/.github/prompts/nano-add-data-provider.prompt.md b/Api.PolicyHeaders.Cors/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.PolicyHeaders.Cors/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.PolicyHeaders.Cors/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.PolicyHeaders.Cors/AGENTS.md b/Api.PolicyHeaders.Cors/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.PolicyHeaders.Cors/AGENTS.md +++ b/Api.PolicyHeaders.Cors/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.PolicyHeaders.ForwardedHeaders/.claude/skills/nano-add-data-provider/SKILL.md b/Api.PolicyHeaders.ForwardedHeaders/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.PolicyHeaders.ForwardedHeaders/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.PolicyHeaders.ForwardedHeaders/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.PolicyHeaders.ForwardedHeaders/.github/prompts/nano-add-data-provider.prompt.md b/Api.PolicyHeaders.ForwardedHeaders/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.PolicyHeaders.ForwardedHeaders/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.PolicyHeaders.ForwardedHeaders/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.PolicyHeaders.ForwardedHeaders/AGENTS.md b/Api.PolicyHeaders.ForwardedHeaders/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.PolicyHeaders.ForwardedHeaders/AGENTS.md +++ b/Api.PolicyHeaders.ForwardedHeaders/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.PolicyHeaders.FrameOptions/.claude/skills/nano-add-data-provider/SKILL.md b/Api.PolicyHeaders.FrameOptions/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.PolicyHeaders.FrameOptions/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.PolicyHeaders.FrameOptions/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.PolicyHeaders.FrameOptions/.github/prompts/nano-add-data-provider.prompt.md b/Api.PolicyHeaders.FrameOptions/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.PolicyHeaders.FrameOptions/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.PolicyHeaders.FrameOptions/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.PolicyHeaders.FrameOptions/AGENTS.md b/Api.PolicyHeaders.FrameOptions/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.PolicyHeaders.FrameOptions/AGENTS.md +++ b/Api.PolicyHeaders.FrameOptions/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.PolicyHeaders.Hsts/.claude/skills/nano-add-data-provider/SKILL.md b/Api.PolicyHeaders.Hsts/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.PolicyHeaders.Hsts/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.PolicyHeaders.Hsts/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.PolicyHeaders.Hsts/.github/prompts/nano-add-data-provider.prompt.md b/Api.PolicyHeaders.Hsts/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.PolicyHeaders.Hsts/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.PolicyHeaders.Hsts/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.PolicyHeaders.Hsts/AGENTS.md b/Api.PolicyHeaders.Hsts/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.PolicyHeaders.Hsts/AGENTS.md +++ b/Api.PolicyHeaders.Hsts/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.PolicyHeaders.ReferrerPolicy/.claude/skills/nano-add-data-provider/SKILL.md b/Api.PolicyHeaders.ReferrerPolicy/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.PolicyHeaders.ReferrerPolicy/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.PolicyHeaders.ReferrerPolicy/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.PolicyHeaders.ReferrerPolicy/.github/prompts/nano-add-data-provider.prompt.md b/Api.PolicyHeaders.ReferrerPolicy/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.PolicyHeaders.ReferrerPolicy/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.PolicyHeaders.ReferrerPolicy/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.PolicyHeaders.ReferrerPolicy/AGENTS.md b/Api.PolicyHeaders.ReferrerPolicy/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.PolicyHeaders.ReferrerPolicy/AGENTS.md +++ b/Api.PolicyHeaders.ReferrerPolicy/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.PolicyHeaders.Robots/.claude/skills/nano-add-data-provider/SKILL.md b/Api.PolicyHeaders.Robots/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.PolicyHeaders.Robots/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.PolicyHeaders.Robots/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.PolicyHeaders.Robots/.github/prompts/nano-add-data-provider.prompt.md b/Api.PolicyHeaders.Robots/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.PolicyHeaders.Robots/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.PolicyHeaders.Robots/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.PolicyHeaders.Robots/AGENTS.md b/Api.PolicyHeaders.Robots/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.PolicyHeaders.Robots/AGENTS.md +++ b/Api.PolicyHeaders.Robots/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.PolicyHeaders.XssProtection/.claude/skills/nano-add-data-provider/SKILL.md b/Api.PolicyHeaders.XssProtection/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.PolicyHeaders.XssProtection/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.PolicyHeaders.XssProtection/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.PolicyHeaders.XssProtection/.github/prompts/nano-add-data-provider.prompt.md b/Api.PolicyHeaders.XssProtection/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.PolicyHeaders.XssProtection/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.PolicyHeaders.XssProtection/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.PolicyHeaders.XssProtection/AGENTS.md b/Api.PolicyHeaders.XssProtection/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.PolicyHeaders.XssProtection/AGENTS.md +++ b/Api.PolicyHeaders.XssProtection/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.RequestTracing/.claude/skills/nano-add-data-provider/SKILL.md b/Api.RequestTracing/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.RequestTracing/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.RequestTracing/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.RequestTracing/.github/prompts/nano-add-data-provider.prompt.md b/Api.RequestTracing/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.RequestTracing/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.RequestTracing/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.RequestTracing/AGENTS.md b/Api.RequestTracing/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.RequestTracing/AGENTS.md +++ b/Api.RequestTracing/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.ResponseCache/.claude/skills/nano-add-data-provider/SKILL.md b/Api.ResponseCache/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.ResponseCache/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.ResponseCache/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.ResponseCache/.github/prompts/nano-add-data-provider.prompt.md b/Api.ResponseCache/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.ResponseCache/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.ResponseCache/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.ResponseCache/AGENTS.md b/Api.ResponseCache/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.ResponseCache/AGENTS.md +++ b/Api.ResponseCache/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.ResponseCompression/.claude/skills/nano-add-data-provider/SKILL.md b/Api.ResponseCompression/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.ResponseCompression/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.ResponseCompression/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.ResponseCompression/.github/prompts/nano-add-data-provider.prompt.md b/Api.ResponseCompression/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.ResponseCompression/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.ResponseCompression/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.ResponseCompression/AGENTS.md b/Api.ResponseCompression/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.ResponseCompression/AGENTS.md +++ b/Api.ResponseCompression/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Session/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Session/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Session/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Session/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Session/.github/prompts/nano-add-data-provider.prompt.md b/Api.Session/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Session/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Session/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Session/AGENTS.md b/Api.Session/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Session/AGENTS.md +++ b/Api.Session/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.StartupTasks/.claude/skills/nano-add-data-provider/SKILL.md b/Api.StartupTasks/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.StartupTasks/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.StartupTasks/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.StartupTasks/.github/prompts/nano-add-data-provider.prompt.md b/Api.StartupTasks/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.StartupTasks/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.StartupTasks/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.StartupTasks/AGENTS.md b/Api.StartupTasks/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.StartupTasks/AGENTS.md +++ b/Api.StartupTasks/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.StaticFiles/.claude/skills/nano-add-data-provider/SKILL.md b/Api.StaticFiles/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.StaticFiles/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.StaticFiles/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.StaticFiles/.github/prompts/nano-add-data-provider.prompt.md b/Api.StaticFiles/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.StaticFiles/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.StaticFiles/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.StaticFiles/AGENTS.md b/Api.StaticFiles/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.StaticFiles/AGENTS.md +++ b/Api.StaticFiles/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Storage.Azure/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Storage.Azure/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Storage.Azure/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Storage.Azure/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Storage.Azure/.github/prompts/nano-add-data-provider.prompt.md b/Api.Storage.Azure/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Storage.Azure/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Storage.Azure/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Storage.Azure/AGENTS.md b/Api.Storage.Azure/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Storage.Azure/AGENTS.md +++ b/Api.Storage.Azure/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Storage.Local/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Storage.Local/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Storage.Local/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Storage.Local/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Storage.Local/.github/prompts/nano-add-data-provider.prompt.md b/Api.Storage.Local/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Storage.Local/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Storage.Local/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Storage.Local/AGENTS.md b/Api.Storage.Local/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Storage.Local/AGENTS.md +++ b/Api.Storage.Local/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.TimeZone/.claude/skills/nano-add-data-provider/SKILL.md b/Api.TimeZone/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.TimeZone/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.TimeZone/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.TimeZone/.github/prompts/nano-add-data-provider.prompt.md b/Api.TimeZone/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.TimeZone/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.TimeZone/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.TimeZone/AGENTS.md b/Api.TimeZone/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.TimeZone/AGENTS.md +++ b/Api.TimeZone/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.Versioning/.claude/skills/nano-add-data-provider/SKILL.md b/Api.Versioning/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.Versioning/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.Versioning/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Versioning/.github/prompts/nano-add-data-provider.prompt.md b/Api.Versioning/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.Versioning/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.Versioning/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.Versioning/AGENTS.md b/Api.Versioning/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.Versioning/AGENTS.md +++ b/Api.Versioning/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api.VirusScan/.claude/skills/nano-add-data-provider/SKILL.md b/Api.VirusScan/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api.VirusScan/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api.VirusScan/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.VirusScan/.github/prompts/nano-add-data-provider.prompt.md b/Api.VirusScan/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api.VirusScan/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api.VirusScan/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api.VirusScan/AGENTS.md b/Api.VirusScan/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api.VirusScan/AGENTS.md +++ b/Api.VirusScan/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Api._Blank/.claude/skills/nano-add-data-provider/SKILL.md b/Api._Blank/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Api._Blank/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Api._Blank/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api._Blank/.github/prompts/nano-add-data-provider.prompt.md b/Api._Blank/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Api._Blank/.github/prompts/nano-add-data-provider.prompt.md +++ b/Api._Blank/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Api._Blank/AGENTS.md b/Api._Blank/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Api._Blank/AGENTS.md +++ b/Api._Blank/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Console.CustomConfigSection/.claude/skills/nano-add-data-provider/SKILL.md b/Console.CustomConfigSection/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Console.CustomConfigSection/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Console.CustomConfigSection/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.CustomConfigSection/.github/prompts/nano-add-data-provider.prompt.md b/Console.CustomConfigSection/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Console.CustomConfigSection/.github/prompts/nano-add-data-provider.prompt.md +++ b/Console.CustomConfigSection/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.CustomConfigSection/AGENTS.md b/Console.CustomConfigSection/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Console.CustomConfigSection/AGENTS.md +++ b/Console.CustomConfigSection/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Console.CustomService/.claude/skills/nano-add-data-provider/SKILL.md b/Console.CustomService/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Console.CustomService/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Console.CustomService/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.CustomService/.github/prompts/nano-add-data-provider.prompt.md b/Console.CustomService/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Console.CustomService/.github/prompts/nano-add-data-provider.prompt.md +++ b/Console.CustomService/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.CustomService/AGENTS.md b/Console.CustomService/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Console.CustomService/AGENTS.md +++ b/Console.CustomService/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Console.Data.InMemory/.claude/skills/nano-add-data-provider/SKILL.md b/Console.Data.InMemory/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Console.Data.InMemory/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Console.Data.InMemory/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.Data.InMemory/.github/prompts/nano-add-data-provider.prompt.md b/Console.Data.InMemory/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Console.Data.InMemory/.github/prompts/nano-add-data-provider.prompt.md +++ b/Console.Data.InMemory/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.Data.InMemory/AGENTS.md b/Console.Data.InMemory/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Console.Data.InMemory/AGENTS.md +++ b/Console.Data.InMemory/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Console.Data.MySql/.claude/skills/nano-add-data-provider/SKILL.md b/Console.Data.MySql/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Console.Data.MySql/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Console.Data.MySql/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.Data.MySql/.github/prompts/nano-add-data-provider.prompt.md b/Console.Data.MySql/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Console.Data.MySql/.github/prompts/nano-add-data-provider.prompt.md +++ b/Console.Data.MySql/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.Data.MySql/AGENTS.md b/Console.Data.MySql/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Console.Data.MySql/AGENTS.md +++ b/Console.Data.MySql/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Console.Data.PostgreSQL/.claude/skills/nano-add-data-provider/SKILL.md b/Console.Data.PostgreSQL/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Console.Data.PostgreSQL/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Console.Data.PostgreSQL/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.Data.PostgreSQL/.github/prompts/nano-add-data-provider.prompt.md b/Console.Data.PostgreSQL/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Console.Data.PostgreSQL/.github/prompts/nano-add-data-provider.prompt.md +++ b/Console.Data.PostgreSQL/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.Data.PostgreSQL/AGENTS.md b/Console.Data.PostgreSQL/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Console.Data.PostgreSQL/AGENTS.md +++ b/Console.Data.PostgreSQL/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Console.Data.SqLite/.claude/skills/nano-add-data-provider/SKILL.md b/Console.Data.SqLite/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Console.Data.SqLite/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Console.Data.SqLite/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.Data.SqLite/.github/prompts/nano-add-data-provider.prompt.md b/Console.Data.SqLite/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Console.Data.SqLite/.github/prompts/nano-add-data-provider.prompt.md +++ b/Console.Data.SqLite/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.Data.SqLite/AGENTS.md b/Console.Data.SqLite/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Console.Data.SqLite/AGENTS.md +++ b/Console.Data.SqLite/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Console.Data.SqlServer/.claude/skills/nano-add-data-provider/SKILL.md b/Console.Data.SqlServer/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Console.Data.SqlServer/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Console.Data.SqlServer/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.Data.SqlServer/.github/prompts/nano-add-data-provider.prompt.md b/Console.Data.SqlServer/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Console.Data.SqlServer/.github/prompts/nano-add-data-provider.prompt.md +++ b/Console.Data.SqlServer/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.Data.SqlServer/AGENTS.md b/Console.Data.SqlServer/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Console.Data.SqlServer/AGENTS.md +++ b/Console.Data.SqlServer/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Console.Eventing.RabbitMq/.claude/skills/nano-add-data-provider/SKILL.md b/Console.Eventing.RabbitMq/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Console.Eventing.RabbitMq/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Console.Eventing.RabbitMq/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.Eventing.RabbitMq/.github/prompts/nano-add-data-provider.prompt.md b/Console.Eventing.RabbitMq/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Console.Eventing.RabbitMq/.github/prompts/nano-add-data-provider.prompt.md +++ b/Console.Eventing.RabbitMq/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.Eventing.RabbitMq/AGENTS.md b/Console.Eventing.RabbitMq/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Console.Eventing.RabbitMq/AGENTS.md +++ b/Console.Eventing.RabbitMq/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Console.ExceptionHandling/.claude/skills/nano-add-data-provider/SKILL.md b/Console.ExceptionHandling/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Console.ExceptionHandling/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Console.ExceptionHandling/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.ExceptionHandling/.github/prompts/nano-add-data-provider.prompt.md b/Console.ExceptionHandling/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Console.ExceptionHandling/.github/prompts/nano-add-data-provider.prompt.md +++ b/Console.ExceptionHandling/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.ExceptionHandling/AGENTS.md b/Console.ExceptionHandling/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Console.ExceptionHandling/AGENTS.md +++ b/Console.ExceptionHandling/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Console.Localization/.claude/skills/nano-add-data-provider/SKILL.md b/Console.Localization/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Console.Localization/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Console.Localization/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.Localization/.github/prompts/nano-add-data-provider.prompt.md b/Console.Localization/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Console.Localization/.github/prompts/nano-add-data-provider.prompt.md +++ b/Console.Localization/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.Localization/AGENTS.md b/Console.Localization/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Console.Localization/AGENTS.md +++ b/Console.Localization/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Console.Logging.Log4Net/.claude/skills/nano-add-data-provider/SKILL.md b/Console.Logging.Log4Net/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Console.Logging.Log4Net/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Console.Logging.Log4Net/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.Logging.Log4Net/.github/prompts/nano-add-data-provider.prompt.md b/Console.Logging.Log4Net/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Console.Logging.Log4Net/.github/prompts/nano-add-data-provider.prompt.md +++ b/Console.Logging.Log4Net/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.Logging.Log4Net/AGENTS.md b/Console.Logging.Log4Net/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Console.Logging.Log4Net/AGENTS.md +++ b/Console.Logging.Log4Net/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Console.Logging.Microsoft/.claude/skills/nano-add-data-provider/SKILL.md b/Console.Logging.Microsoft/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Console.Logging.Microsoft/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Console.Logging.Microsoft/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.Logging.Microsoft/.github/prompts/nano-add-data-provider.prompt.md b/Console.Logging.Microsoft/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Console.Logging.Microsoft/.github/prompts/nano-add-data-provider.prompt.md +++ b/Console.Logging.Microsoft/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.Logging.Microsoft/AGENTS.md b/Console.Logging.Microsoft/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Console.Logging.Microsoft/AGENTS.md +++ b/Console.Logging.Microsoft/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Console.Logging.NLog/.claude/skills/nano-add-data-provider/SKILL.md b/Console.Logging.NLog/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Console.Logging.NLog/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Console.Logging.NLog/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.Logging.NLog/.github/prompts/nano-add-data-provider.prompt.md b/Console.Logging.NLog/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Console.Logging.NLog/.github/prompts/nano-add-data-provider.prompt.md +++ b/Console.Logging.NLog/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.Logging.NLog/AGENTS.md b/Console.Logging.NLog/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Console.Logging.NLog/AGENTS.md +++ b/Console.Logging.NLog/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Console.Logging.Serilog/.claude/skills/nano-add-data-provider/SKILL.md b/Console.Logging.Serilog/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Console.Logging.Serilog/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Console.Logging.Serilog/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.Logging.Serilog/.github/prompts/nano-add-data-provider.prompt.md b/Console.Logging.Serilog/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Console.Logging.Serilog/.github/prompts/nano-add-data-provider.prompt.md +++ b/Console.Logging.Serilog/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.Logging.Serilog/AGENTS.md b/Console.Logging.Serilog/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Console.Logging.Serilog/AGENTS.md +++ b/Console.Logging.Serilog/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Console.StartupTasks/.claude/skills/nano-add-data-provider/SKILL.md b/Console.StartupTasks/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Console.StartupTasks/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Console.StartupTasks/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.StartupTasks/.github/prompts/nano-add-data-provider.prompt.md b/Console.StartupTasks/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Console.StartupTasks/.github/prompts/nano-add-data-provider.prompt.md +++ b/Console.StartupTasks/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.StartupTasks/AGENTS.md b/Console.StartupTasks/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Console.StartupTasks/AGENTS.md +++ b/Console.StartupTasks/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Console.Storage.Azure/.claude/skills/nano-add-data-provider/SKILL.md b/Console.Storage.Azure/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Console.Storage.Azure/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Console.Storage.Azure/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.Storage.Azure/.github/prompts/nano-add-data-provider.prompt.md b/Console.Storage.Azure/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Console.Storage.Azure/.github/prompts/nano-add-data-provider.prompt.md +++ b/Console.Storage.Azure/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.Storage.Azure/AGENTS.md b/Console.Storage.Azure/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Console.Storage.Azure/AGENTS.md +++ b/Console.Storage.Azure/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Console.Storage.Local/.claude/skills/nano-add-data-provider/SKILL.md b/Console.Storage.Local/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Console.Storage.Local/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Console.Storage.Local/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.Storage.Local/.github/prompts/nano-add-data-provider.prompt.md b/Console.Storage.Local/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Console.Storage.Local/.github/prompts/nano-add-data-provider.prompt.md +++ b/Console.Storage.Local/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.Storage.Local/AGENTS.md b/Console.Storage.Local/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Console.Storage.Local/AGENTS.md +++ b/Console.Storage.Local/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Console.Workers/.claude/skills/nano-add-data-provider/SKILL.md b/Console.Workers/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Console.Workers/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Console.Workers/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.Workers/.github/prompts/nano-add-data-provider.prompt.md b/Console.Workers/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Console.Workers/.github/prompts/nano-add-data-provider.prompt.md +++ b/Console.Workers/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console.Workers/AGENTS.md b/Console.Workers/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Console.Workers/AGENTS.md +++ b/Console.Workers/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Console._Blank/.claude/skills/nano-add-data-provider/SKILL.md b/Console._Blank/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Console._Blank/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Console._Blank/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console._Blank/.github/prompts/nano-add-data-provider.prompt.md b/Console._Blank/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Console._Blank/.github/prompts/nano-add-data-provider.prompt.md +++ b/Console._Blank/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Console._Blank/AGENTS.md b/Console._Blank/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Console._Blank/AGENTS.md +++ b/Console._Blank/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes diff --git a/Web._Blank/.claude/skills/nano-add-data-provider/SKILL.md b/Web._Blank/.claude/skills/nano-add-data-provider/SKILL.md index eb27cca8..d6d849c6 100644 --- a/Web._Blank/.claude/skills/nano-add-data-provider/SKILL.md +++ b/Web._Blank/.claude/skills/nano-add-data-provider/SKILL.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives — a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Web._Blank/.github/prompts/nano-add-data-provider.prompt.md b/Web._Blank/.github/prompts/nano-add-data-provider.prompt.md index f617ab71..8770612e 100644 --- a/Web._Blank/.github/prompts/nano-add-data-provider.prompt.md +++ b/Web._Blank/.github/prompts/nano-add-data-provider.prompt.md @@ -105,6 +105,11 @@ in `Development` so `dotnet ef` commands from a local shell still work; the dock service name wouldn't resolve outside the compose network at all. Use step 6's derived database name for the connection string's `Database=`/equivalent value. +For PostgreSQL, add `GSS Encryption Mode=Disable` to every connection string (this local one, and the +ones in the Staging/Production steps below). Npgsql otherwise tries Kerberos/GSS encryption first, and +the .NET Docker images have no `libgssapi_krb5`, so the app logs `Cannot load library +libgssapi_krb5.so.2` at start-up. These apps do not use Kerberos, so disabling it loses nothing. + ⚠ Add only the chosen provider's connection string, active. Don't add the other providers' connection strings as commented-out alternatives - a project commits to exactly one provider, and commented dead alternatives for providers it doesn't use are clutter, not documentation. If a @@ -338,7 +343,7 @@ Provisioning that server is out of this skill's scope. $env:SQL_USER = az postgres flexible-server microsoft-entra-admin list -g $env:AZURE_GROUP_DATABASE -s $env:SQL_SERVER --query "[0].principalName" -o tsv; $env:SQL_TOKEN = az account get-access-token --resource-type oss-rdbms --query accessToken -o tsv; - $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true"; + $env:DATA__CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_TOKEN;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; & "/opt/ef-tools/$env:DOTNET_EF_TOOLS_VERSION/dotnet-ef" database update ` --no-build ` @@ -374,7 +379,7 @@ Provisioning that server is out of this skill's scope. az postgres flexible-server execute -n $env:SQL_SERVER -u $env:SQL_USER -p $env:SQL_TOKEN -d $env:SQL_NAME --file-path $env:GRANTS_SQL_PATH; if ($LastExitCode -ne 0) { throw "error"; }; - $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true"; + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:IDENTITY_NAME;SSL Mode=Require;Trust Server Certificate=true;GSS Encryption Mode=Disable"; echo "SQL_CONNECTIONSTRING=$env:SQL_CONNECTIONSTRING" >> $env:GITHUB_ENV; ``` diff --git a/Web._Blank/AGENTS.md b/Web._Blank/AGENTS.md index e8e8b237..1a2db10c 100644 --- a/Web._Blank/AGENTS.md +++ b/Web._Blank/AGENTS.md @@ -26,7 +26,7 @@ step. ## Solution Structure Every Nano application — Api, Web, or Console — follows the same predictable solution layout. `{name}` is the -application's own name (e.g. `Svc.Accounts`); `{name}.Models` is a **separate, sibling project**, not nested +application's own name (e.g. `MyService`); `{name}.Models` is a **separate, sibling project**, not nested inside `{name}/`. | Directory / File | API | WEB | CON | Description | @@ -390,7 +390,7 @@ own `.docker/docker-compose.yml` — not just the config. **Applies equally to Console applications.** This isn't a Public/Admin-API-only concern — a Console app (a run-to-completion job or worker) consuming an Api Client needs its target runnable locally exactly the same way -(e.g. a sign-up job calling `Svc.Accounts`/`Svc.Emailing`). The only difference is a Console app's own compose +(e.g. a sign-up job calling an accounts service and an emailing service). The only difference is a Console app's own compose service has no `ports` of its own to collide with (no HTTP surface — see [Authentication forwarding](#authentication-forwarding)'s note that Console workers typically call only anonymous endpoints, or need `LogInRoot`); the nested dependency services still need their own unique host ports, same as for an API/Web consumer. @@ -1950,8 +1950,8 @@ Two genuinely different roles a controller plays in a layered Nano solution — (both are ordinary `BaseController`/`BaseEntityController<...>` subclasses), but a naming/design one worth being explicit about, since it changes what an action's body actually does: -- **Public API controller** — the customer/end-user-facing application (e.g. `Api.Platform`/`Api.Admin` in this - solution). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no +- **Public API controller** — the customer/end-user-facing application (the application that exposes the + solution's services to its users). Its actions compose one or more injected [Api Clients](#api-clients) into a response; it has no `IRepository` of its own. This is the intended shape, not an absolute rule enforced anywhere — a Data, Storage, or Eventing provider *can* be added directly to a Public API if genuinely needed (`nano-add-data-provider`/ `nano-add-storage-provider`/`nano-add-eventing-provider` all allow it), but doing so pulls the app away from @@ -1999,8 +1999,7 @@ is actively dangerous, not just architecturally unusual: A Public API that needs to offer login/signup/password-management to end users does so by **composing calls to the internal service that actually owns Identity**, through that service's Api Client (its `.Identity`/`.Auth` method groups — see [Api Clients § Built-in method groups](#built-in-method-groups)), or by implementing its -own transient auth with server-computed claims (see `Api.Admin`'s `AccountsController` in this codebase for a -working example) — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the +own transient auth with server-computed claims — never by hosting `BaseEntityUserController`/persistent `BaseAuthController` on the Public API itself. #### Entity controller hierarchy @@ -2048,17 +2047,13 @@ shapes. **Default to assign / remove** and use a collection only when one of the the link doesn't already exist) are made by the Public API before the generic create or delete. - **Collection (exception).** The request carries the whole set and an explicit server action replaces it in one transaction (diff the existing rows against the requested ones, then delete and add). Use it only when: - - the set is edited as a reviewed batch with an explicit Save (for example a role's permissions, where every - live toggle would change access immediately and one Save is one audit entry), or - - a rule can only be checked against the final set, or - - the parent is meaningless without its first children and is created with them. + - the set is edited as a reviewed batch with an explicit Save, where every live change would take effect + immediately and one Save is one audit entry, or + - a rule can only be checked against the final set. A collection update must be an explicit action: the generic edit routes can't change a collection (see [Include Annotation](#include-annotation)). An empty set is valid unless a rule says otherwise. -Pick one shape per relationship and keep both its create and update on it. Don't offer a collection on create and -assign / remove for later edits of the same relationship unless create genuinely needs its first children. - #### Query criteria The second generic parameter defines what's queryable. Derive from `BaseQueryCriteria` (already contributes From d1fc3c5b85ce0de93ebb1035be3ee163d1b02b3a Mon Sep 17 00:00:00 2001 From: vivet Date: Thu, 29 Oct 2026 19:33:11 +0100 Subject: [PATCH 2/2] Updated --- Api.ApiClients.Audit/AGENTS.md | 6 +++++- Api.ApiClients.Entity/AGENTS.md | 6 +++++- Api.ApiClients.RootLogIn/AGENTS.md | 6 +++++- Api.ApiClients/AGENTS.md | 6 +++++- Api.Auth.External.Custom/AGENTS.md | 6 +++++- Api.Auth.External.Microsoft/AGENTS.md | 6 +++++- Api.Auth.RootLogin/AGENTS.md | 6 +++++- Api.Authorization/AGENTS.md | 6 +++++- Api.ContentNegotiation/AGENTS.md | 6 +++++- Api.Cookies/AGENTS.md | 6 +++++- Api.CustomConfigSection/AGENTS.md | 6 +++++- Api.CustomMiddleware/AGENTS.md | 6 +++++- Api.CustomService/AGENTS.md | 6 +++++- Api.Data.Audit/AGENTS.md | 6 +++++- Api.Data.EntityEvents/AGENTS.md | 6 +++++- Api.Data.Identity.Auth.ApiKey/AGENTS.md | 6 +++++- Api.Data.Identity.Auth.External.Custom/AGENTS.md | 6 +++++- Api.Data.Identity.Auth.Jwt/AGENTS.md | 6 +++++- Api.Data.Identity/AGENTS.md | 6 +++++- Api.Data.InMemory/AGENTS.md | 6 +++++- Api.Data.LazyLoading/AGENTS.md | 6 +++++- Api.Data.MySql.Collation/AGENTS.md | 6 +++++- Api.Data.MySql.Mappings/AGENTS.md | 6 +++++- Api.Data.MySql.Spatial/AGENTS.md | 6 +++++- Api.Data.MySql.StoredProcedures/AGENTS.md | 6 +++++- Api.Data.MySql.Views/AGENTS.md | 6 +++++- Api.Data.MySql/AGENTS.md | 6 +++++- Api.Data.PostgreSQL.Spatial/AGENTS.md | 6 +++++- Api.Data.PostgreSQL/AGENTS.md | 6 +++++- Api.Data.Repository.AutoSave/AGENTS.md | 6 +++++- Api.Data.Repository.Includes/AGENTS.md | 6 +++++- Api.Data.SoftDelete/AGENTS.md | 6 +++++- Api.Data.SqLite/AGENTS.md | 6 +++++- Api.Data.SqlServer.Spatial/AGENTS.md | 6 +++++- Api.Data.SqlServer/AGENTS.md | 6 +++++- Api.Data.Triggers/AGENTS.md | 6 +++++- Api.Documentation.Csp/AGENTS.md | 6 +++++- Api.Documentation/AGENTS.md | 6 +++++- Api.ErrorHandling/AGENTS.md | 6 +++++- Api.Eventing.RabbitMq/AGENTS.md | 6 +++++- Api.HealthChecks/AGENTS.md | 6 +++++- Api.Hosting.Http/AGENTS.md | 6 +++++- Api.Hosting.Https/AGENTS.md | 6 +++++- Api.Hosting.MultipartLimits/AGENTS.md | 6 +++++- Api.Localization/AGENTS.md | 6 +++++- Api.Logging.Log4Net/AGENTS.md | 6 +++++- Api.Logging.Microsoft/AGENTS.md | 6 +++++- Api.Logging.NLog/AGENTS.md | 6 +++++- Api.Logging.Serilog/AGENTS.md | 6 +++++- Api.Metrics/AGENTS.md | 6 +++++- Api.MultipartJson/AGENTS.md | 6 +++++- Api.PolicyHeaders.ContentSecurityPolicy/AGENTS.md | 6 +++++- Api.PolicyHeaders.ContentTypeOptions/AGENTS.md | 6 +++++- Api.PolicyHeaders.Cors/AGENTS.md | 6 +++++- Api.PolicyHeaders.ForwardedHeaders/AGENTS.md | 6 +++++- Api.PolicyHeaders.FrameOptions/AGENTS.md | 6 +++++- Api.PolicyHeaders.Hsts/AGENTS.md | 6 +++++- Api.PolicyHeaders.ReferrerPolicy/AGENTS.md | 6 +++++- Api.PolicyHeaders.Robots/AGENTS.md | 6 +++++- Api.PolicyHeaders.XssProtection/AGENTS.md | 6 +++++- Api.RequestTracing/AGENTS.md | 6 +++++- Api.ResponseCache/AGENTS.md | 6 +++++- Api.ResponseCompression/AGENTS.md | 6 +++++- Api.Session/AGENTS.md | 6 +++++- Api.StartupTasks/AGENTS.md | 6 +++++- Api.StaticFiles/AGENTS.md | 6 +++++- Api.Storage.Azure/AGENTS.md | 6 +++++- Api.Storage.Local/AGENTS.md | 6 +++++- Api.TimeZone/AGENTS.md | 6 +++++- Api.Versioning/AGENTS.md | 6 +++++- Api.VirusScan/AGENTS.md | 6 +++++- Api._Blank/AGENTS.md | 6 +++++- Console.CustomConfigSection/AGENTS.md | 6 +++++- Console.CustomService/AGENTS.md | 6 +++++- Console.Data.InMemory/AGENTS.md | 6 +++++- Console.Data.MySql/AGENTS.md | 6 +++++- Console.Data.PostgreSQL/AGENTS.md | 6 +++++- Console.Data.SqLite/AGENTS.md | 6 +++++- Console.Data.SqlServer/AGENTS.md | 6 +++++- Console.Eventing.RabbitMq/AGENTS.md | 6 +++++- Console.ExceptionHandling/AGENTS.md | 6 +++++- Console.Localization/AGENTS.md | 6 +++++- Console.Logging.Log4Net/AGENTS.md | 6 +++++- Console.Logging.Microsoft/AGENTS.md | 6 +++++- Console.Logging.NLog/AGENTS.md | 6 +++++- Console.Logging.Serilog/AGENTS.md | 6 +++++- Console.StartupTasks/AGENTS.md | 6 +++++- Console.Storage.Azure/AGENTS.md | 6 +++++- Console.Storage.Local/AGENTS.md | 6 +++++- Console.Workers/AGENTS.md | 6 +++++- Console._Blank/AGENTS.md | 6 +++++- Web._Blank/AGENTS.md | 6 +++++- 92 files changed, 460 insertions(+), 92 deletions(-) diff --git a/Api.ApiClients.Audit/AGENTS.md b/Api.ApiClients.Audit/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.ApiClients.Audit/AGENTS.md +++ b/Api.ApiClients.Audit/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.ApiClients.Entity/AGENTS.md b/Api.ApiClients.Entity/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.ApiClients.Entity/AGENTS.md +++ b/Api.ApiClients.Entity/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.ApiClients.RootLogIn/AGENTS.md b/Api.ApiClients.RootLogIn/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.ApiClients.RootLogIn/AGENTS.md +++ b/Api.ApiClients.RootLogIn/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.ApiClients/AGENTS.md b/Api.ApiClients/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.ApiClients/AGENTS.md +++ b/Api.ApiClients/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Auth.External.Custom/AGENTS.md b/Api.Auth.External.Custom/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Auth.External.Custom/AGENTS.md +++ b/Api.Auth.External.Custom/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Auth.External.Microsoft/AGENTS.md b/Api.Auth.External.Microsoft/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Auth.External.Microsoft/AGENTS.md +++ b/Api.Auth.External.Microsoft/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Auth.RootLogin/AGENTS.md b/Api.Auth.RootLogin/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Auth.RootLogin/AGENTS.md +++ b/Api.Auth.RootLogin/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Authorization/AGENTS.md b/Api.Authorization/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Authorization/AGENTS.md +++ b/Api.Authorization/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.ContentNegotiation/AGENTS.md b/Api.ContentNegotiation/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.ContentNegotiation/AGENTS.md +++ b/Api.ContentNegotiation/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Cookies/AGENTS.md b/Api.Cookies/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Cookies/AGENTS.md +++ b/Api.Cookies/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.CustomConfigSection/AGENTS.md b/Api.CustomConfigSection/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.CustomConfigSection/AGENTS.md +++ b/Api.CustomConfigSection/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.CustomMiddleware/AGENTS.md b/Api.CustomMiddleware/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.CustomMiddleware/AGENTS.md +++ b/Api.CustomMiddleware/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.CustomService/AGENTS.md b/Api.CustomService/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.CustomService/AGENTS.md +++ b/Api.CustomService/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Data.Audit/AGENTS.md b/Api.Data.Audit/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Data.Audit/AGENTS.md +++ b/Api.Data.Audit/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Data.EntityEvents/AGENTS.md b/Api.Data.EntityEvents/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Data.EntityEvents/AGENTS.md +++ b/Api.Data.EntityEvents/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Data.Identity.Auth.ApiKey/AGENTS.md b/Api.Data.Identity.Auth.ApiKey/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Data.Identity.Auth.ApiKey/AGENTS.md +++ b/Api.Data.Identity.Auth.ApiKey/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Data.Identity.Auth.External.Custom/AGENTS.md b/Api.Data.Identity.Auth.External.Custom/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Data.Identity.Auth.External.Custom/AGENTS.md +++ b/Api.Data.Identity.Auth.External.Custom/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Data.Identity.Auth.Jwt/AGENTS.md b/Api.Data.Identity.Auth.Jwt/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Data.Identity.Auth.Jwt/AGENTS.md +++ b/Api.Data.Identity.Auth.Jwt/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Data.Identity/AGENTS.md b/Api.Data.Identity/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Data.Identity/AGENTS.md +++ b/Api.Data.Identity/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Data.InMemory/AGENTS.md b/Api.Data.InMemory/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Data.InMemory/AGENTS.md +++ b/Api.Data.InMemory/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Data.LazyLoading/AGENTS.md b/Api.Data.LazyLoading/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Data.LazyLoading/AGENTS.md +++ b/Api.Data.LazyLoading/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Data.MySql.Collation/AGENTS.md b/Api.Data.MySql.Collation/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Data.MySql.Collation/AGENTS.md +++ b/Api.Data.MySql.Collation/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Data.MySql.Mappings/AGENTS.md b/Api.Data.MySql.Mappings/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Data.MySql.Mappings/AGENTS.md +++ b/Api.Data.MySql.Mappings/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Data.MySql.Spatial/AGENTS.md b/Api.Data.MySql.Spatial/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Data.MySql.Spatial/AGENTS.md +++ b/Api.Data.MySql.Spatial/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Data.MySql.StoredProcedures/AGENTS.md b/Api.Data.MySql.StoredProcedures/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Data.MySql.StoredProcedures/AGENTS.md +++ b/Api.Data.MySql.StoredProcedures/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Data.MySql.Views/AGENTS.md b/Api.Data.MySql.Views/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Data.MySql.Views/AGENTS.md +++ b/Api.Data.MySql.Views/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Data.MySql/AGENTS.md b/Api.Data.MySql/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Data.MySql/AGENTS.md +++ b/Api.Data.MySql/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Data.PostgreSQL.Spatial/AGENTS.md b/Api.Data.PostgreSQL.Spatial/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Data.PostgreSQL.Spatial/AGENTS.md +++ b/Api.Data.PostgreSQL.Spatial/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Data.PostgreSQL/AGENTS.md b/Api.Data.PostgreSQL/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Data.PostgreSQL/AGENTS.md +++ b/Api.Data.PostgreSQL/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Data.Repository.AutoSave/AGENTS.md b/Api.Data.Repository.AutoSave/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Data.Repository.AutoSave/AGENTS.md +++ b/Api.Data.Repository.AutoSave/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Data.Repository.Includes/AGENTS.md b/Api.Data.Repository.Includes/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Data.Repository.Includes/AGENTS.md +++ b/Api.Data.Repository.Includes/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Data.SoftDelete/AGENTS.md b/Api.Data.SoftDelete/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Data.SoftDelete/AGENTS.md +++ b/Api.Data.SoftDelete/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Data.SqLite/AGENTS.md b/Api.Data.SqLite/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Data.SqLite/AGENTS.md +++ b/Api.Data.SqLite/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Data.SqlServer.Spatial/AGENTS.md b/Api.Data.SqlServer.Spatial/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Data.SqlServer.Spatial/AGENTS.md +++ b/Api.Data.SqlServer.Spatial/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Data.SqlServer/AGENTS.md b/Api.Data.SqlServer/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Data.SqlServer/AGENTS.md +++ b/Api.Data.SqlServer/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Data.Triggers/AGENTS.md b/Api.Data.Triggers/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Data.Triggers/AGENTS.md +++ b/Api.Data.Triggers/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Documentation.Csp/AGENTS.md b/Api.Documentation.Csp/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Documentation.Csp/AGENTS.md +++ b/Api.Documentation.Csp/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Documentation/AGENTS.md b/Api.Documentation/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Documentation/AGENTS.md +++ b/Api.Documentation/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.ErrorHandling/AGENTS.md b/Api.ErrorHandling/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.ErrorHandling/AGENTS.md +++ b/Api.ErrorHandling/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Eventing.RabbitMq/AGENTS.md b/Api.Eventing.RabbitMq/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Eventing.RabbitMq/AGENTS.md +++ b/Api.Eventing.RabbitMq/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.HealthChecks/AGENTS.md b/Api.HealthChecks/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.HealthChecks/AGENTS.md +++ b/Api.HealthChecks/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Hosting.Http/AGENTS.md b/Api.Hosting.Http/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Hosting.Http/AGENTS.md +++ b/Api.Hosting.Http/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Hosting.Https/AGENTS.md b/Api.Hosting.Https/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Hosting.Https/AGENTS.md +++ b/Api.Hosting.Https/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Hosting.MultipartLimits/AGENTS.md b/Api.Hosting.MultipartLimits/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Hosting.MultipartLimits/AGENTS.md +++ b/Api.Hosting.MultipartLimits/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Localization/AGENTS.md b/Api.Localization/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Localization/AGENTS.md +++ b/Api.Localization/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Logging.Log4Net/AGENTS.md b/Api.Logging.Log4Net/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Logging.Log4Net/AGENTS.md +++ b/Api.Logging.Log4Net/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Logging.Microsoft/AGENTS.md b/Api.Logging.Microsoft/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Logging.Microsoft/AGENTS.md +++ b/Api.Logging.Microsoft/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Logging.NLog/AGENTS.md b/Api.Logging.NLog/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Logging.NLog/AGENTS.md +++ b/Api.Logging.NLog/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Logging.Serilog/AGENTS.md b/Api.Logging.Serilog/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Logging.Serilog/AGENTS.md +++ b/Api.Logging.Serilog/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Metrics/AGENTS.md b/Api.Metrics/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Metrics/AGENTS.md +++ b/Api.Metrics/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.MultipartJson/AGENTS.md b/Api.MultipartJson/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.MultipartJson/AGENTS.md +++ b/Api.MultipartJson/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/AGENTS.md b/Api.PolicyHeaders.ContentSecurityPolicy/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.PolicyHeaders.ContentSecurityPolicy/AGENTS.md +++ b/Api.PolicyHeaders.ContentSecurityPolicy/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.PolicyHeaders.ContentTypeOptions/AGENTS.md b/Api.PolicyHeaders.ContentTypeOptions/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.PolicyHeaders.ContentTypeOptions/AGENTS.md +++ b/Api.PolicyHeaders.ContentTypeOptions/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.PolicyHeaders.Cors/AGENTS.md b/Api.PolicyHeaders.Cors/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.PolicyHeaders.Cors/AGENTS.md +++ b/Api.PolicyHeaders.Cors/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.PolicyHeaders.ForwardedHeaders/AGENTS.md b/Api.PolicyHeaders.ForwardedHeaders/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.PolicyHeaders.ForwardedHeaders/AGENTS.md +++ b/Api.PolicyHeaders.ForwardedHeaders/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.PolicyHeaders.FrameOptions/AGENTS.md b/Api.PolicyHeaders.FrameOptions/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.PolicyHeaders.FrameOptions/AGENTS.md +++ b/Api.PolicyHeaders.FrameOptions/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.PolicyHeaders.Hsts/AGENTS.md b/Api.PolicyHeaders.Hsts/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.PolicyHeaders.Hsts/AGENTS.md +++ b/Api.PolicyHeaders.Hsts/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.PolicyHeaders.ReferrerPolicy/AGENTS.md b/Api.PolicyHeaders.ReferrerPolicy/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.PolicyHeaders.ReferrerPolicy/AGENTS.md +++ b/Api.PolicyHeaders.ReferrerPolicy/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.PolicyHeaders.Robots/AGENTS.md b/Api.PolicyHeaders.Robots/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.PolicyHeaders.Robots/AGENTS.md +++ b/Api.PolicyHeaders.Robots/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.PolicyHeaders.XssProtection/AGENTS.md b/Api.PolicyHeaders.XssProtection/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.PolicyHeaders.XssProtection/AGENTS.md +++ b/Api.PolicyHeaders.XssProtection/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.RequestTracing/AGENTS.md b/Api.RequestTracing/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.RequestTracing/AGENTS.md +++ b/Api.RequestTracing/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.ResponseCache/AGENTS.md b/Api.ResponseCache/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.ResponseCache/AGENTS.md +++ b/Api.ResponseCache/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.ResponseCompression/AGENTS.md b/Api.ResponseCompression/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.ResponseCompression/AGENTS.md +++ b/Api.ResponseCompression/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Session/AGENTS.md b/Api.Session/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Session/AGENTS.md +++ b/Api.Session/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.StartupTasks/AGENTS.md b/Api.StartupTasks/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.StartupTasks/AGENTS.md +++ b/Api.StartupTasks/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.StaticFiles/AGENTS.md b/Api.StaticFiles/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.StaticFiles/AGENTS.md +++ b/Api.StaticFiles/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Storage.Azure/AGENTS.md b/Api.Storage.Azure/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Storage.Azure/AGENTS.md +++ b/Api.Storage.Azure/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Storage.Local/AGENTS.md b/Api.Storage.Local/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Storage.Local/AGENTS.md +++ b/Api.Storage.Local/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.TimeZone/AGENTS.md b/Api.TimeZone/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.TimeZone/AGENTS.md +++ b/Api.TimeZone/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.Versioning/AGENTS.md b/Api.Versioning/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.Versioning/AGENTS.md +++ b/Api.Versioning/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api.VirusScan/AGENTS.md b/Api.VirusScan/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api.VirusScan/AGENTS.md +++ b/Api.VirusScan/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Api._Blank/AGENTS.md b/Api._Blank/AGENTS.md index 1a2db10c..65129246 100644 --- a/Api._Blank/AGENTS.md +++ b/Api._Blank/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Console.CustomConfigSection/AGENTS.md b/Console.CustomConfigSection/AGENTS.md index 1a2db10c..65129246 100644 --- a/Console.CustomConfigSection/AGENTS.md +++ b/Console.CustomConfigSection/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Console.CustomService/AGENTS.md b/Console.CustomService/AGENTS.md index 1a2db10c..65129246 100644 --- a/Console.CustomService/AGENTS.md +++ b/Console.CustomService/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Console.Data.InMemory/AGENTS.md b/Console.Data.InMemory/AGENTS.md index 1a2db10c..65129246 100644 --- a/Console.Data.InMemory/AGENTS.md +++ b/Console.Data.InMemory/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Console.Data.MySql/AGENTS.md b/Console.Data.MySql/AGENTS.md index 1a2db10c..65129246 100644 --- a/Console.Data.MySql/AGENTS.md +++ b/Console.Data.MySql/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Console.Data.PostgreSQL/AGENTS.md b/Console.Data.PostgreSQL/AGENTS.md index 1a2db10c..65129246 100644 --- a/Console.Data.PostgreSQL/AGENTS.md +++ b/Console.Data.PostgreSQL/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Console.Data.SqLite/AGENTS.md b/Console.Data.SqLite/AGENTS.md index 1a2db10c..65129246 100644 --- a/Console.Data.SqLite/AGENTS.md +++ b/Console.Data.SqLite/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Console.Data.SqlServer/AGENTS.md b/Console.Data.SqlServer/AGENTS.md index 1a2db10c..65129246 100644 --- a/Console.Data.SqlServer/AGENTS.md +++ b/Console.Data.SqlServer/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Console.Eventing.RabbitMq/AGENTS.md b/Console.Eventing.RabbitMq/AGENTS.md index 1a2db10c..65129246 100644 --- a/Console.Eventing.RabbitMq/AGENTS.md +++ b/Console.Eventing.RabbitMq/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Console.ExceptionHandling/AGENTS.md b/Console.ExceptionHandling/AGENTS.md index 1a2db10c..65129246 100644 --- a/Console.ExceptionHandling/AGENTS.md +++ b/Console.ExceptionHandling/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Console.Localization/AGENTS.md b/Console.Localization/AGENTS.md index 1a2db10c..65129246 100644 --- a/Console.Localization/AGENTS.md +++ b/Console.Localization/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Console.Logging.Log4Net/AGENTS.md b/Console.Logging.Log4Net/AGENTS.md index 1a2db10c..65129246 100644 --- a/Console.Logging.Log4Net/AGENTS.md +++ b/Console.Logging.Log4Net/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Console.Logging.Microsoft/AGENTS.md b/Console.Logging.Microsoft/AGENTS.md index 1a2db10c..65129246 100644 --- a/Console.Logging.Microsoft/AGENTS.md +++ b/Console.Logging.Microsoft/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Console.Logging.NLog/AGENTS.md b/Console.Logging.NLog/AGENTS.md index 1a2db10c..65129246 100644 --- a/Console.Logging.NLog/AGENTS.md +++ b/Console.Logging.NLog/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Console.Logging.Serilog/AGENTS.md b/Console.Logging.Serilog/AGENTS.md index 1a2db10c..65129246 100644 --- a/Console.Logging.Serilog/AGENTS.md +++ b/Console.Logging.Serilog/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Console.StartupTasks/AGENTS.md b/Console.StartupTasks/AGENTS.md index 1a2db10c..65129246 100644 --- a/Console.StartupTasks/AGENTS.md +++ b/Console.StartupTasks/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Console.Storage.Azure/AGENTS.md b/Console.Storage.Azure/AGENTS.md index 1a2db10c..65129246 100644 --- a/Console.Storage.Azure/AGENTS.md +++ b/Console.Storage.Azure/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Console.Storage.Local/AGENTS.md b/Console.Storage.Local/AGENTS.md index 1a2db10c..65129246 100644 --- a/Console.Storage.Local/AGENTS.md +++ b/Console.Storage.Local/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Console.Workers/AGENTS.md b/Console.Workers/AGENTS.md index 1a2db10c..65129246 100644 --- a/Console.Workers/AGENTS.md +++ b/Console.Workers/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Console._Blank/AGENTS.md b/Console._Blank/AGENTS.md index 1a2db10c..65129246 100644 --- a/Console._Blank/AGENTS.md +++ b/Console._Blank/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. | diff --git a/Web._Blank/AGENTS.md b/Web._Blank/AGENTS.md index 1a2db10c..65129246 100644 --- a/Web._Blank/AGENTS.md +++ b/Web._Blank/AGENTS.md @@ -1415,6 +1415,10 @@ Enables the Swagger UI at `/docs`. } ``` +The OpenAPI file describes the API exactly as the code is, so a client (such as one generated with `orval`) is generated straight from it and the file is **never edited by hand or patched**. If the file is wrong, the code is fixed: +- `nullable` is set only for a property that is nullable in the code, a nullable enum included (`allOf` the enum, with `nullable: true`). +- A property is `required` when it is not nullable, a value type (number, bool, enum) included, or when it is marked `[Required]` or `required`. This is the same in a response and in a request body. + ⚠ When using a strict CSP, add style hash `sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=` — otherwise Swagger UI's icons/images fail to render. @@ -2854,7 +2858,7 @@ IEntitySoftDeletable : IEntityDeletable, redeclares IsDeleted (opt-in) | `BaseEntityDeletable` / `` | `IEntityDeletable` only | | | `BaseEntityCreatableAndUpdatable` / `` | Create + update, no delete | | | `BaseEntityCreatableAndDeletable` / `` | Create + delete, no update | For entities only ever added and removed, such as a join row between two entities. | -| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` + `[SwaggerRequestIgnore]` — always eager-loaded, never validated as input, never shown in Swagger request bodies. See [Identity](#identity). | +| `BaseEntityUser` / `` | Update + delete (`IEntityUser`) | Adds `IdentityUser` (`IdentityUserEx`), tagged `[Include]` + `[ValidateNever]` — always eager-loaded and never validated as input. Swagger documents a user request body as a separate `{Name}Request` schema without `IdentityUser`, so a client can not send it. See [Identity](#identity). | | `BaseEntityView` | None — bare `IEntity`, no `Id`/`IsDeleted`/`CreatedAt` at all | Non-generic only. For entities mapped to a SQL view — you define every property yourself, including any identifier. | | `BaseEntityIdentity` / `` | `Id` only, nothing else | For advanced cases that don't want the built-in `IsDeleted`/`CreatedAt` — implement whichever capability interface (`IEntityCreatable`, `IEntityWritable`, etc.) yourself to restore the operations you need. |