Configuration
Primary defaults live in appsettings.json. For local development, override with appsettings.Development.json and user secrets or environment variables. For production (IIS or container), prefer environment variables and keep secrets out of repo.
Connection strings
"ConnectionStrings": {
"DefaultConnection": "Server=sql-host,1433;Database=customstuf;User Id=app;Password=***;TrustServerCertificate=true",
"TarbelConnection": "Server=sql-host,1433;Database=tarbel;User Id=app;Password=***;TrustServerCertificate=true"
}
appsettings.Development.json is committed and must only ever contain the REPLACE_WITH_MSSQL_SA_PASSWORD placeholders — never a real password. For local development put the real connection strings in user secrets (loaded automatically for the Development and dv environments):
cd src/CustomsHive.Web
dotnet user-secrets set "ConnectionStrings:DefaultConnection" "Server=...;Database=customstuf_dv;User Id=...;Password=...;TrustServerCertificate=true"
dotnet user-secrets set "ConnectionStrings:TarbelConnection" "Server=...;Database=tarbel;User Id=...;Password=...;TrustServerCertificate=true"
The Tarbel database is managed by the application. Admins upload the ZIP from the minfin portal via /Admin/Tarbel; the TarbelImportWorker imports GoodsNomenclature, GeographicalArea, MeasureType, AdditionalCode, Certificate, Measure, and related tables into the Tarbel database configured by TarbelConnection.
Auth
Set Auth:Provider to select the authentication backend:
| Value | Backend |
|---|---|
EntraId |
Microsoft Entra ID (Azure AD) |
| (empty) | Treated as Entra ID |
Entra ID
"Auth": {
"Provider": "EntraId",
"EntraId": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "",
"ClientId": "",
"ClientSecret": "",
"CallbackPath": "/signin-oidc",
"CertificateThumbprint": "" // optional — use cert instead of secret
}
}
Only Entra ID authentication is supported by the current code.
AI provider
Set AI:Provider to either AzureOpenAI or OpenAI.
AzureOpenAI: Azure endpoint + deploymentOpenAI: OpenAI API key and optional OpenAI-compatibleBaseUrl- If
AI:Provideris empty, the app auto-detects provider from configured keys
"AI": {
"Provider": "AzureOpenAI",
"AzureOpenAI": {
"Endpoint": "https://your-resource.openai.azure.com/",
"DeploymentName": "gpt-4o",
"UseKeylessAuth": true, // true = app registration (ClientSecretCredential)
"ApiKey": "" // set if UseKeylessAuth = false
},
"OpenAI": {
"BaseUrl": "",
"ApiKey": "",
"Model": "gpt-4o-mini"
}
}
When UseKeylessAuth is true, the client authenticates via ClientSecretCredential using the shared Azure:* credentials block (see below). When false, ApiKey is required.
Azure credentials (shared)
Both Azure OpenAI and Azure Document Intelligence use the same app registration when UseKeylessAuth is true:
Azure Document Intelligence
Used for native PDF OCR/layout analysis before sending text to Azure OpenAI. Optional — if not configured, the app falls back to GPT vision (PDF rasterised to JPEG).
"DocumentIntelligence": {
"Endpoint": "https://your-resource.cognitiveservices.azure.com/",
"UseKeylessAuth": true, // uses Azure:* credentials block above
"ApiKey": "", // set if UseKeylessAuth = false
"ModelId": "prebuilt-layout"
}
| Key | Default | Description |
|---|---|---|
Endpoint |
— | Required. Azure Document Intelligence resource endpoint URL |
UseKeylessAuth |
true |
When true, uses shared Azure:* ClientSecretCredential |
ApiKey |
— | Required when UseKeylessAuth is false |
ModelId |
prebuilt-layout |
Document model to use for analysis |
Storage
UploadFolder is where uploaded PDFs/XLSX files and temp upload files are stored.
Reverse proxy trust
"Proxy": {
"KnownNetworks": [ "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "127.0.0.0/8", "::1/128" ]
}
CIDR networks whose peers are trusted to set X-Forwarded-For / X-Forwarded-Proto (the reverse proxy). Requests from outside these networks have their forwarded headers ignored, so internet clients cannot spoof their IP. Docker env-var form: Proxy__KnownNetworks__0=10.0.0.0/8 etc. If your proxy runs on a public IP, add its address as a /32.
Branding
Branding is configured from appsettings.json (not from Admin settings):
"Branding": {
"TitlePlain": "CustomsTUF",
"TitleHtml": "<span class=\"brand-prefix\">Customs</span><span class=\"brand-suffix\">TUF</span>",
"MainColor": "#119E50"
}
TitlePlain: plain-text title (e.g. login page and metadata)TitleHtml: rendered in the main layout headerMainColor: global accent color
Descartes SMF XSD validation
SMF schema validation is optional and controlled by config:
For Docker images built from this repo:
- schemas/smf/*.xsd are bundled into publish output
- runtime defaults are set via env vars in Dockerfile:
- Descartes__Smf__ValidateXsd=true
- Descartes__Smf__XsdFolder=/app/schemas/smf
Processing
PdfDpi controls the resolution at which PDFs are rasterised before being sent to the AI vision model. 300 DPI is recommended — lower values cause digit misreads.
Country aliases
The AI sometimes returns country names that differ from the Tarbel database entries (e.g. TURKIYE instead of TURKEY, USA instead of UNITED STATES). Add aliases here without a rebuild:
Keys are case-insensitive. Values must be valid ISO 3166-1 alpha-2 codes. When the AI returns a name not in the Tarbel GeographicalArea table, the resolution order is:
- Already a 2-letter ISO2 code → pass through
CountryAliaseslookup (this section)- Exact match on
DescriptionENorDescriptionNLin Tarbel - Partial / starts-with match in Tarbel
- Log warning, leave as-is
Service Bus — ingestion queue
Configures the inbound canonical queue consumed by CanonicalServiceBusReceiverService. Uses the shared Azure:* credentials block.
"ServiceBus": {
"Ingestion": {
"ActiveEnvironment": "prd",
"prd": {
"FullyQualifiedNamespace": "your-servicebus.servicebus.windows.net",
"QueueName": "customshive-inbound"
}
}
}
Multiple named environments (dv, stg, prd) can be defined; only ActiveEnvironment is connected at runtime. Connectivity can be tested via /Admin/IngestionSettings → Run peek probe.
| Key | Description |
|---|---|
ServiceBus:Ingestion:ActiveEnvironment |
Name of the active environment block |
ServiceBus:Ingestion:{env}:FullyQualifiedNamespace |
Service Bus namespace FQDN |
ServiceBus:Ingestion:{env}:QueueName |
Inbound queue name |
Ingestion API keys
API keys for POST /api/declarations are managed entirely via /Admin/ApiKeys — no appsettings entry is needed. Keys are stored hashed (SHA-256); the raw key is shown only once at creation.
See ingestion-api.md for usage details.
Declarations (company-wide defaults)
Stored in the AppSettings table (editable via /Admin/DeclarationFields), not in appsettings.json. Prefix: declarations:.
| Key | Description |
|---|---|
declarations:sender_gln |
GLN of the sender (MessageHeader) |
declarations:declarant_eori |
Declarant / representative EORI |
declarations:declarant_name |
Declarant name as it appears in XML declarations |
declarations:declarant_street |
Declarant street and house number |
declarations:declarant_city |
Declarant city |
declarations:declarant_postcode |
Declarant postcode (also issuingPlaceCode) |
declarations:crin |
Declarant CRIN reference number (Descartes SMF) |
declarations:auth_c501 |
Authorisation C501 reference (AEO-C) |
declarations:auth_c505 |
Authorisation C505 reference (guarantee) |
declarations:auth_c517 |
Authorisation C517 reference (AGP) |
declarations:auth_c506 |
Authorisation C506 reference (REM) |
declarations:supervising_customs_office |
Default supervising customs office ref |
declarations:warehouse |
Default warehouse identifier (H2B) |
declarations:location_of_goods |
Default UN/LOCODE for location of goods |
declarations:type_part_three |
TypePartThree code in SMF declarations (e.g. T2LF) |
declarations:contact_phone |
Contact phone number for declarations |
declarations:contact_name |
Contact person name for declarations |
declarations:contact_email |
Contact person email for declarations |
Ingestion settings
Stored in the AppSettings table (editable via /Admin/IngestionSettings). Prefix: ingestion:.
| Key | Default | Description |
|---|---|---|
ingestion:auto_dispatch |
false |
When true, automatically sends the generated NCTS T2 XML to the Descartes outbound queue after a dossier is created from a canonical ingestion message. |
Tarbel settings
Stored in the AppSettings table (editable via /Admin/Tarbel).
| Key | Default | Description |
|---|---|---|
tarbel:auto_download |
false |
When true, automatically downloads and imports any pending Tarbel extractions during the daily sync cycle (runs at 02:00 UTC). When false, the worker still refreshes the latest available extraction key each night (so the admin banner stays current) but does not download. The download can always be triggered manually via the Download & import button in /Admin/Tarbel. |
UN/LOCODE settings
Stored in the AppSettings table (editable via /Admin/Locode).
| Key | Default | Description |
|---|---|---|
locode:auto_sync |
false |
When true, automatically checks and re-imports UN/LOCODE data when a new version is published. |
locode:datapackage_url |
(datahub.io URL) | URL of the datapackage.json used to check for a new version. |
locode:csv_url |
(datahub.io URL) | Direct URL of the code-list.csv file to download. |