Caution
Evaluate before upgrading production. This release targets .NET 11 RC1, which has Microsoft's go-live support license and is not GA.
Changes
This release adds OIDC sign-in, compliance with the current MCP specification, OpenAPI 3.2 support, and tenant headers. Telemetry configuration is now consolidated under a single key (Telemetry:Provider), routing traces and metrics to an OTLP collector or making them available for Prometheus scraping at /metrics. Request metrics now include the associated endpoint name.
Breaking changes
- .NET 11 runtime requirement: Upgraded target runtime to .NET 11 (RC1).
- License change (#19): Relicensed project from AGPL-3.0 to EUPL-1.2.
- Webhook routing structure: Replaced root route
POST /api/{env}/webhook/{id}with namespaced routes (POST /api/{env}/{namespace}/{name}/{id}). Updated default configuration path toIntegrations/Inbound. - Proxy OData documentation visibility: Hidden OData query parameters on proxy endpoints by default. Set
"SupportsOData": trueinentity.jsonto expose them in documentation. - Web UI authentication model: Replaced Admin API Key login with account-based authentication. Initial startup generates an
adminaccount with a single-use password printed to logs; mandatory password update required on first login. The legacy API key is not reused. Reset passwords viaportway accounts password <username> <new-password>(bare metal) ordocker exec <container> dotnet /app/PortwayApi.dll accounts password <username> <new-password>(Docker).
New in v0.7.0
- .NET 11: runtime target
net11.0(RC1, go-live support license). - Tenant headers:
Tenancyon an endpoint maps a request header to a column, function parameter, upstream header or file directory placeholder.AllowedTenantson a token lists the permitted values per header; the request header selects one. SQL reads,$count, writes, table-valued functions, stored procedures, proxy requests and file directories are restricted to that value. Supported in the console, the OpenAPI document and the MCPtenantsargument. - OIDC: OIDC providers are registered in the console and grant console sign-in.
- PostgreSQL and MySQL parity (#29): SQL endpoints behave identically on SQL Server, PostgreSQL and MySQL/MariaDB.
- Direct table write mode (#42): SQL endpoints write to a table with validation and full CRUD, without a stored procedure.
- OData
$expand(#43): a to-one relationship inentity.jsonjoins the related record on Table and View endpoints, limited to the target endpoint's allowlist. - OData 8 query engine: the SQL query stack uses Microsoft.OData 8 and SqlKata 4 through the
Melosso.DynamicODataToSQLfork. - OpenAPI 3.2: QUERY (RFC 10008) is documented as a native operation. Namespaces form a tag hierarchy. Endpoints support
deprecatedand configurable example payloads. File uploads document their multipart encoding. All errors share one response schema./docs(Scalar 1.72.1) renders nested namespaces as a sidebar tree and documents QUERY and MERGE. - GraphQL over QUERY: method translation with
"AllowedMethods": ["QUERY"]serves cached, read-only GraphQL proxying. - Telemetry providers (#33):
Telemetry:ProviderselectsOtlp(push) orPrometheus(scrape endpoint, unauthenticated and rate-limit exempt like/health).Enabled/OtlpEndpointconfigurations remain supported. The console settings page displays the active provider. - Per-endpoint metrics (#33): request duration histograms carry a
portway.endpointtag. New cache hit and miss counters. - Per-token rate limiting: each token enforces its own limit (
RateLimitRequests,RateLimitWindowSeconds); tokens without one use the globalRateLimiting:TokenLimit. - Response transforms (#34, #41):
ResponseTransformson proxy, composite and SQL endpoints appliesRemove,RenameandMaskrules to top-level JSON fields. Masked values return***; cached responses are stored after transformation. - MCP per endpoint:
Mcp.Exposedopts an endpoint into MCP andMcp.Instructionadds usage instructions to its tool description. - Namespaced File and Webhook endpoints: both types support namespaces.
Fixes
- Security: file ids were unencrypted (
base64(env:path)) and could be forged to read or delete any file in an environment. File ids are AES-GCM encrypted withPORTWAY_ENCRYPTION_KEY; downloads, deletes and listings are restricted to the endpoint'sBaseDirectory. - Security: client headers named like an environment header or an
HttpMethodAppendHeadersheader were forwarded with the configured value. Configured headers replace them. - Security: client query parameters overrode credentials in an endpoint
Urlwith the same name. Colliding client parameters are dropped. - Security: authenticated responses could be stored by shared caches. Proxy responses defaulted to
Cache-Control: publicand passed an upstreampublicthrough. Authenticated responses areprivate, andVarylistsAuthorizationand the endpoint's tenant headers. - Security: rate limit bucket keys are hashed; raw tokens are not stored.
- Security: stored procedure names are excluded from the OpenAPI document.
- Security: server headers are stripped from proxied responses.
- Security: file uploads are checked for base directory containment and extension.
- Security: failover candidates and composite steps are revalidated at call time.
- Security:
/docsmakes no third-party requests. Scalar's hosted fonts, telemetry, AI agent (api.scalar.com), MCP generator and developer toolbar are disabled; fonts (Onest) are served by the gateway; the page sends the gatewayContent-Security-Policy. - Security: various security fixes.
- Fix: with
FileStorage:UseMemoryCache(default) an upload was confirmed before it was saved to disk; a crash within 30 seconds lost it. Uploads are saved to a temporary file, flushed and renamed before the response. Concurrent uploads of a new name withoutoverwritereturn one201and409for the others. The memory cache serves reads only; temporary files left by a crash are deleted at startup. - Fix: overlapping timer ticks during file flush and indexing corrupted or truncated files under heavy disk activity.
- Fix: relative
BaseDirectoryvalues and upload subpaths were ignored; files were saved to the environment root. Files are saved to the configured directory. - Fix: files uploaded to an absolute
BaseDirectorycould not be downloaded, deleted or listed. All three operations work within that directory. - Fix: two config saves within the same millisecond collided on the backup file name; one was lost.
- Fix: saving an unchanged config file created a backup and displaced one of the ten kept versions.
- Fix: the token column upgrade was skipped for
auth.dbfiles with aTokenstable and noTokenAuditstable. - Fix: webhook inserts used SQL Server syntax and failed on PostgreSQL, MySQL/MariaDB and SQLite. The insert statement is generated per provider.
- Fix:
Propertiesin a SQLentity.json(MaxPageSize,DefaultSort,CacheEnabled) were not loaded;$topwas not limited byMaxPageSize. The values are applied. - Fix: hot reload of
settings.jsonon a live instance caused transient request rejections and authorization failures. - Fix: proxy requests with a
charsetin the Content-Type returned500. The forwarded Content-Type is parsed. - Fix: XML and SOAP bodies to proxy endpoints returned
415. Content validation is selected by endpoint type instead of URL text. - Fix: path segments appended after a query string in an endpoint
Urlproduced invalid upstream URLs. - Fix: malformed OData requests return
400instead of500. - Fix:
MERGEis served end to end and documented underadditionalOperations. It was rendered asPATCHwithout a route. - Fix: namespaces nested deeper than one level are routed.
ParseEndpointprobed a single segment. - Fix: each namespace segment is validated separately; nested namespaces failed their own naming rules.
- Fix: file listings returned
500when an upload changed the file index during the listing. Listings read a snapshot taken under the index lock. - Fix: file endpoint namespace handling and error responses.
- Fix: the identifier pattern for PostgreSQL stored procedure parameters accepts all valid parameter names.
- Fix: webhook tag descriptions referenced a removed root configuration.
- Fix: HMAC environment authentication blocked thread-pool threads while reading request bodies. Body reads are asynchronous.
- Fix: requests with unique URL paths grew the cache lock table without bound. The table is bounded.
- Fix: file cache byte counts drifted under concurrent traffic. Counts are atomic and the memory cap is enforced.
- Fix: Redis connection state flags desynchronized across threads and delayed reconnection after network failures.
- Fix: host shutdown and restart threw unhandled disposal exceptions or deadlocked.
StopAsyncand connection pool cleanup complete without errors. - Fix: a failing database maintenance service stopped the host. Maintenance errors are logged and retried.
- Fix: file watcher and SQL pool shutdown swallowed errors; disposing either twice threw.
- Fix: locked environment
settings.jsonfiles are read again with retries on load. - Fix: the log was closed at the start of shutdown; messages written while requests drained were lost.
- Fix: timeouts and cache limits in configuration are enforced at runtime.
- Fix: the telemetry pipeline started only with an OTLP collector configured. Metrics work with either provider.
- Fix: token authentication and rate limiting blocked the Prometheus scrape endpoint. It is exempt like
/health. - Fix: MCP chat streamed no events. The chat delta record had an enum converter that failed serialization.
- Fix: MCP page asset paths, script loading and the health endpoint in the console.
- Fix: token creation and validation in the console ran blocking checks; both are asynchronous.
- Fix: OData parameters are documented only on proxy endpoints with
SupportsOData. - Fix: the bearer scheme is documented as
httpinstead ofapiKey, without the JWT claim. - Fix: intermediate tags are linked for namespaces nested three levels or more. The ancestor pass ran against a stale snapshot.
- Fix: CSV and XML examples keep their
serializedValueformat instead of being converted to JSON strings. - Fix: the
/docsbreadcrumb bar displayed the first nested namespace (e.g.WMS > Inbound) in every section. The bar is hidden pending a Scalar fix. - Fix: a port in use fails the start before database and endpoint initialization.
- Fix: startup exceptions were missing from console output. The log template includes exception details.
- Fix: the startup environment warning ignored
DOTNET_ENVIRONMENTand--environment; the Key Vault status ignoredPORTWAY_KEYVAULT_URI. - Fix: unreachable backends logged an error per endpoint, environment and retry. Health checks and SQL metadata log one summary line; details are logged at
Debug. - Fix: startup logging is reduced to a one-line title and hosting summary; the endpoint tree, pool, SQL provider and reload lines are logged at
Debug. - Fix: the startup log listed
localhost/127.0.0.1twice under allowed hosts. Hosts are deduplicated. - Fix: the
.backupsfolder is hidden on Windows, like.core. - Fix: unused and redundant configuration settings are removed.
- Fix: unused code and NuGet packages are removed; the build output is smaller.
- Fix: obsolete API warning during build.
Upgrade Notes
- Header precedence: Environment headers (
environments/{env}/settings.jsonHeaders) andHttpMethodAppendHeadersoverride client headers of the same name. IfAuthorizationis set at the environment level, clientAuthorizationheaders are dropped. Legacy Teable and AFASX-API-Keyconfigurations remain supported but are optional. - Database schema: Initial startup adds
Tokens.AllowedTenants(default{}) toauth.db. Pre-existing tokens contain empty tenant arrays and will be rejected by endpoints requiringTenancy. - File storage path: File endpoints configured with a relative
BaseDirectorystore new uploads in that specific folder. Existing files located in the environment root remain functional only after manual migration to the target directory. - File identifier format: Legacy file IDs remain valid on endpoints lacking a
BaseDirectory. Endpoints specifying aBaseDirectoryissue updated IDs, which depend onPORTWAY_ENCRYPTION_KEYand invalidate if the key rotates. - PostgreSQL webhook schema: Webhook tables in PostgreSQL must use quoted column names
"Id","WebhookId","Payload", and"ReceivedAt"(TIMESTAMPTZ). Refer to the webhook guide for provider-specific table definitions. - Prometheus endpoint security: The Prometheus metrics endpoint exposes metrics only when using the
Prometheusprovider. The endpoint is publicly accessible by design and must be secured via firewall or reverse proxy in exposed environments. - Environment variable naming: Configuration variables now standardise on the
PORTWAY_prefix (e.g.,PORTWAY_ALLOWED_HOSTS,PORTWAY_ADMIN_KEY,PORTWAY_PATH_BASE). Legacy unprefixed variables continue to function but emit deprecation warnings. - Telemetry configuration: Existing telemetry schemas remain backward-compatible;
"Enabled": truedefaults to OTLP and reads flatOtlpEndpointproperties. Migrating toTelemetry:Provideris recommended.
Setup guide: documentation.
Full Changelog: v0.6.1...v0.7.0