Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. The format

## [Unreleased]

### Added

- [#81](https://github.com/mohanagy/miftah/issues/81) Added configuration format v3 and a strict, non-secret OAuth connection core: opaque profile/upstream/resource/issuer bindings, canonical HTTPS Streamable HTTP validation, static-Authorization collision refusal, OS-vault credential isolation, crash-released binding-scoped transaction coordination, redacted lifecycle primitives, and v1/v2-to-v3 migration with no credential synthesis. Native OAuth remains intentionally disabled until the separate discovery, browser, callback, exchange, and header-injection work is complete; v3 OAuth declarations fail closed with `OAUTH_AUTHORIZATION_NOT_ENABLED`.

## [0.3.3] - 2026-07-22

### Fixed
Expand Down
4 changes: 2 additions & 2 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Architecture

The [threat model](threat-model.md) maps these components and their boundaries to public security controls and residual risks. The [OAuth and Console security design delta](oauth-console-threat-model.md) describes future-only control-plane and credential-lifecycle gates; it does not add a current runtime component.
The [threat model](threat-model.md) maps these components and their boundaries to public security controls and residual risks. The [OAuth and Console security design delta](oauth-console-threat-model.md) describes the remaining control-plane and credential-lifecycle gates. Version 3 adds a non-secret connection/vault core, but it does not add an enabled browser, callback, discovery, exchange, or header-injection runtime component.

Miftah is an MCP-aware proxy, not a byte-level reverse proxy:

Expand All @@ -16,7 +16,7 @@ MCP client
-> upstream STDIO server
```

The STDIO public server is built with the official `@modelcontextprotocol/sdk` `Server` and `StdioServerTransport`. Each profile/upstream pair gets an SDK `Client` and its configured transport on first use: local processes use `StdioClientTransport`; remote upstreams use Streamable HTTP by default (the historical `"http"` alias is accepted only by version 1) or the deprecated legacy SSE client. Resolved profile headers override upstream headers case-insensitively before either remote transport is constructed. Remote SDK transports receive those static headers only; they do not receive an OAuth client provider. Streamable HTTP session shutdown sends DELETE while its session ID is still available, then closes the local client transport; legacy SSE has no corresponding server-session teardown request. Request cancellation and requested progress notifications retain their MCP request context across both STDIO and Streamable HTTP upstreams. Startup HTTP failures and operation HTTP/MCP protocol failures are normalized before crossing the proxy boundary, retaining only stable category/code metadata rather than server response text. See [OAuth support](oauth-support.md) for the deliberately unimplemented OAuth lifecycle and its future boundary.
The STDIO public server is built with the official `@modelcontextprotocol/sdk` `Server` and `StdioServerTransport`. Each profile/upstream pair gets an SDK `Client` and its configured transport on first use: local processes use `StdioClientTransport`; remote upstreams use Streamable HTTP by default (the historical `"http"` alias is accepted only by version 1) or the deprecated legacy SSE client. Resolved profile headers override upstream headers case-insensitively before either remote transport is constructed. Remote SDK transports receive those static headers only; they do not receive an OAuth client provider. A version-3 OAuth declaration is rejected before runtime construction with `OAUTH_AUTHORIZATION_NOT_ENABLED`, rather than being ignored or merged with static headers. Streamable HTTP session shutdown sends DELETE while its session ID is still available, then closes the local client transport; legacy SSE has no corresponding server-session teardown request. Request cancellation and requested progress notifications retain their MCP request context across both STDIO and Streamable HTTP upstreams. Startup HTTP failures and operation HTTP/MCP protocol failures are normalized before crossing the proxy boundary, retaining only stable category/code metadata rather than server response text. See [OAuth support](oauth-support.md) for the intentionally incomplete native OAuth boundary.

The optional HTTP host owns the listener and no shared Miftah session state. It validates the exact `/mcp` target, Host, Origin, bearer authentication, a bounded JSON initialize request, and capacity before creating a `StreamableHTTPServerTransport`. That transport and a new `MiftahServer`, `ProfileManager`, and upstream manager belong to one HTTP session only. The host forces the session runtime to in-memory `session` profile state, even if normal STDIO configuration uses workspace/global persistence. Its session registry owns only opaque MCP session IDs, idle timers, and cleanup tasks; DELETE, expiry, connection shutdown, and wrapper shutdown detach the record before asynchronous runtime cleanup so no client can reuse a closing runtime, while retaining that capacity reservation until cleanup succeeds. A rejected cleanup is recorded, retains capacity, and makes graceful shutdown fail rather than silently admitting unlimited replacement runtimes. A reconnect is routed to the existing transport by its session ID, while the SDK permits only one active standalone SSE stream for that session.

Expand Down
4 changes: 2 additions & 2 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,11 @@ Windows profile credential isolation is currently rejected before any runtime fi

## Remote upstream transports

Use `transport: "streamable-http"` for new remote MCP servers. The historical `transport: "http"` value remains a Streamable HTTP compatibility alias only in configuration version 1; version 2 requires `"streamable-http"`. `transport: "sse"` supports legacy SSE servers but is deprecated and should be used only while an upstream has not migrated. `transport: "stdio"` remains the local-process default.
Use `transport: "streamable-http"` for new remote MCP servers. The historical `transport: "http"` value remains a Streamable HTTP compatibility alias only in configuration version 1; versions 2 and 3 require `"streamable-http"`. `transport: "sse"` supports legacy SSE servers but is deprecated and should be used only while an upstream has not migrated. `transport: "stdio"` remains the local-process default.

Remote URLs must use `https`. Miftah accepts `http` only for loopback development URLs on `localhost`, `127.0.0.0/8`, or `::1`; other cleartext URLs and non-HTTP URL schemes fail config validation at the exact `upstream.url` or `upstreams.<name>.url` path. Profile headers override upstream headers case-insensitively, so `Authorization` and `authorization` are one credential slot rather than two combined values.

Remote authentication is static: configure only explicit `headers`/secret references that the upstream documents. There is no OAuth configuration object, callback URL, client-registration setting, browser flow, token refresh, or token revoke command in the current strict schema. Unknown OAuth keys are rejected instead of being silently accepted. See [OAuth support](oauth-support.md) for the exact provider, local-stdio, and future remote-HTTP boundary.
Remote authentication is active only through explicit `headers`/secret references that the upstream documents. Version 3 also accepts a strict non-secret `oauth.connections` declaration for one exact HTTPS Streamable HTTP profile/upstream target, but it rejects token, refresh-token, client-secret, callback, and Authorization-header fields. An OAuth declaration is intentionally fail-closed at runtime with `OAUTH_AUTHORIZATION_NOT_ENABLED` until discovery, browser authorization, exchange, and safe remote-header injection are implemented. Unknown OAuth keys are rejected instead of being silently accepted. See [OAuth support](oauth-support.md) for the exact provider, local-stdio, and remote-HTTP boundary.

Miftah uses Node's normal TLS validation and does not disable certificate verification. A self-signed remote certificate therefore fails closed unless the operator establishes a trusted local CA through the normal Node trust configuration. Do not use cleartext remote HTTP to transport profile credentials.

Expand Down
6 changes: 3 additions & 3 deletions docs/library-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ await runtime.connect(new StdioServerTransport());

## Type exports

The configuration contract exposes `ActiveProfileStateScope`, `AuditConfig`, `AuditIntegrityConfig`, `AuditRotationConfig`, `GitHubProfileRoutingMatch`, `HttpServerConfig`, `IdentityConfig`, `IdentityFingerprint`, `IdentityProbeConfig`, `JiraProfileRoutingMatch`, `LinearProfileRoutingMatch`, `MiftahConfig`, `MiftahConfigVersion`, `PluginConfig`, `PluginKind`, `PluginsConfig`, `PolicyConfig`, `PostHogProfileRoutingMatch`, `ProcessConfig`, `ProfileConfig`, `ProfileIsolationConfig`, `ProfileIsolationContainerVolume`, `ProfileIsolationFile`, `ProfileLeaseConfig`, `ProfileRoutingConfig`, `ProfileRoutingMatchConfig`, `ProfileUpstreamOverride`, `RiskLevel`, `RoutingConfig`, `RoutingMatcherPluginConfig`, `RoutingRule`, `SecurityConfig`, `SecretProviderPluginConfig`, `SentryProfileRoutingMatch`, `SecretsConfig`, `ServerConfig`, `StateConfig`, `ToolDiscoveryMode`, `ToolingConfig`, `TransportType`, `UnknownToolRisk`, `UpstreamConfig`, and `ValidatedRoutingConfig`.
The configuration contract exposes `ActiveProfileStateScope`, `AuditConfig`, `AuditIntegrityConfig`, `AuditRotationConfig`, `GitHubProfileRoutingMatch`, `HttpServerConfig`, `IdentityConfig`, `IdentityFingerprint`, `IdentityProbeConfig`, `JiraProfileRoutingMatch`, `LinearProfileRoutingMatch`, `MiftahConfig`, `MiftahConfigVersion`, `OAuthConfig`, `OAuthConnectionConfig`, `OAuthConnectionRef`, `PluginConfig`, `PluginKind`, `PluginsConfig`, `PolicyConfig`, `PostHogProfileRoutingMatch`, `ProcessConfig`, `ProfileConfig`, `ProfileIsolationConfig`, `ProfileIsolationContainerVolume`, `ProfileIsolationFile`, `ProfileLeaseConfig`, `ProfileRoutingConfig`, `ProfileRoutingMatchConfig`, `ProfileUpstreamOverride`, `RiskLevel`, `RoutingConfig`, `RoutingMatcherPluginConfig`, `RoutingRule`, `SecurityConfig`, `SecretProviderPluginConfig`, `SentryProfileRoutingMatch`, `SecretsConfig`, `ServerConfig`, `StateConfig`, `ToolDiscoveryMode`, `ToolingConfig`, `TransportType`, `UnknownToolRisk`, `UpstreamConfig`, and `ValidatedRoutingConfig`.

`MiftahConfigVersion` is the union of format versions accepted by this installed release. `CURRENT_CONFIG_VERSION` is the version generated by presets; it does not cause `loadConfig` to rewrite a legacy file. Use the explicit [configuration migration command](cli.md#migrate-config) when an on-disk upgrade is intended.

`MiftahConfig` is a version-discriminated union. A `version: "1"` value retains the documented compatibility aliases, while a `version: "2"` value statically accepts only the canonical surface: use `transport: "streamable-http"`, `secrets.allowPlaintextSecrets`, and the always-on redaction behavior. This intentional pre-1.0 type-surface change means application-side composition must use an intersection such as `type AppConfig = MiftahConfig & { readonly appMetadata: AppMetadata }`, rather than `interface AppConfig extends MiftahConfig`; strip application-only metadata before strict Miftah config validation.
`MiftahConfig` is a version-discriminated union. A `version: "1"` value retains the documented compatibility aliases; `version: "2"` accepts the canonical static-credential surface; and `version: "3"` adds opaque `OAuthConfig` bindings. Version 3 contains no token, refresh-token, client-secret, or callback fields, and an OAuth-configured runtime fails closed with `OAUTH_AUTHORIZATION_NOT_ENABLED` until the separate authorization engine exists. This intentional pre-1.0 type-surface change means application-side composition must use an intersection such as `type AppConfig = MiftahConfig & { readonly appMetadata: AppMetadata }`, rather than `interface AppConfig extends MiftahConfig`; strip application-only metadata before strict Miftah config validation.

`AuditRotationConfig` requires `retainFiles` (maximum `2000`) plus at least one positive trigger (`maxBytes` or `maxAgeMs`). `AuditIntegrityConfig` currently exposes the explicit local `"sha256-chain"` option only. Both configure the runtime journal; journal writers, readers, and CLI export/verification implementations remain internal.

Expand All @@ -41,7 +41,7 @@ The configuration contract exposes `ActiveProfileStateScope`, `AuditConfig`, `Au

`UpstreamConfig.trustToolAnnotations` is opt-in and defaults to false. `ToolingConfig.unknownToolRisk` uses the exported `UnknownToolRisk` union (`"write" | "destructive"`) and defaults to `"destructive"`; callers can use exact `toolRiskOverrides` for known read tools.

There is no public OAuth configuration, connection, adapter, client-provider, or token-lifecycle export in this release. `UpstreamConfig.headers` remains the explicit remote credential surface; configuration additions must not imply a supported OAuth workflow until their runtime, secure-store, CLI, and migration contracts exist. See [OAuth support](oauth-support.md).
`OAuthConfig`, `OAuthConnectionConfig`, and the opaque `OAuthConnectionRef` expose only version-3 non-secret connection declarations. There is no public OAuth adapter, browser client, callback, token value, credential-store, or token-lifecycle export. `UpstreamConfig.headers` remains the active remote credential surface; an OAuth declaration does not imply a supported OAuth workflow until its runtime authorization engine exists. See [OAuth support](oauth-support.md).

`ProfileRoutingConfig` describes opt-in identifiers for Miftah's fixed in-tree provider matchers. It is declarative configuration only: it does not load third-party code, resolve secrets, or grant a matcher access to process or network APIs.

Expand Down
2 changes: 1 addition & 1 deletion docs/oauth-console-threat-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

> **Status:** Design-only security decision for [#80](https://github.com/mohanagy/miftah/issues/80). It is a forward-looking delta to the maintainer-authored [threat model](threat-model.md) published for [#37](https://github.com/mohanagy/miftah/issues/37), not evidence that the independent review is complete.

No production OAuth broker, Console, callback listener, or token store exists in this release. The current wrapper still supports only explicit static headers or provider-owned/upstream-owned authentication as described in [OAuth support](oauth-support.md). This document sets the conditions that must be met before an OAuth or Console implementation can be proposed.
No production OAuth broker, Console, callback listener, or enabled token-store integration exists in this release. Version 3 adds a tested internal OS-vault connection contract with binding-scoped, crash-released local transaction coordination, but it cannot be configured into a running authorization flow: no browser, discovery, exchange, or remote Authorization-header injection is enabled. The current wrapper still supports active authentication only through explicit static headers or provider-owned/upstream-owned authentication as described in [OAuth support](oauth-support.md). This document sets the conditions that must be met before an OAuth or Console implementation can be enabled.

The design follows the [MCP Authorization specification](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization), [RFC 8707 resource indicators](https://www.rfc-editor.org/rfc/rfc8707), [RFC 9728 protected-resource metadata](https://www.rfc-editor.org/rfc/rfc9728), [RFC 8414 authorization-server metadata](https://www.rfc-editor.org/rfc/rfc8414), [RFC 8252](https://www.rfc-editor.org/rfc/rfc8252) for native-app browser flows, [RFC 9207 `iss`](https://www.rfc-editor.org/rfc/rfc9207) authorization-response issuer identification, and [RFC 9700](https://www.rfc-editor.org/rfc/rfc9700) for OAuth security practice. If a provider or SDK cannot meet the required properties below, Miftah must not approximate them with a provider-specific shortcut.

Expand Down
Loading