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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. The format

### Added

- [#377](https://github.com/mohanagy/miftah/issues/377) Added the opt-in production profile-context boundary for trusted modern stateless hosts. Short-lived authenticated-encrypted handles bind a named profile to the verified issuer, subject, audience, chat, deployment, and monotonic sealing-key epoch; deployment-wide revocation, exact expiry, removed-profile checks, bearer-free keyed audit correlation, audited rejection and replacement-before-revocation ordering, and fixed fail-closed errors apply on every request. Reserved tool or request metadata is stripped before audit argument capture and upstream forwarding, transition approvals are bound to the authenticated chat correlation, modern discovery enforces identical client-visible tools across profiles and is independent of prior selection calls, and existing stdio plus CLI-owned session-aware HTTP behavior remains unchanged until protocol-era negotiation is enabled separately.
- [#376](https://github.com/mohanagy/miftah/issues/376) Added a public authenticated request-context boundary for future modern stateless handling. Trusted embedding hosts can provide verified issuer, subject, audience, per-chat, issuance, and expiry claims; Miftah derives only opaque deployment-bound and separately keyed audit correlations, fails closed on missing, malformed, expired, or mismatched context, and never falls back to MCP `clientInfo`, arbitrary headers, request metadata, tool arguments, or mutable profile state. The existing CLI-owned Streamable HTTP server remains the legacy session-aware path until a supported host supplies the trusted per-chat claim and modern protocol integration is enabled.

## [1.0.0] - 2026-08-11
Expand Down
23 changes: 21 additions & 2 deletions docs/library-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
| `MIFTAH_VERSION` | The package version compiled into Miftah's CLI and MCP metadata. |
| `CURRENT_CONFIG_VERSION` | The canonical configuration format written by current Miftah presets and examples. |
| `createMiftahRuntime` | Creates an MCP wrapper from a configuration file without exposing process, profile, or server internals. |
| `ProfileContextHandleService` | Mints, resolves, replaces, and revokes short-lived opaque profile selectors for a trusted modern stateless host. |
| `ProfileContextHandleError` | Fixed-code error class that never includes a handle, decrypted payload, identity claim, or backend detail. |
| `InMemoryProfileContextRevocationStore` | Bounded same-process implementation for tests and single-process hosts; it is not deployment-wide storage. |
| `PROFILE_CONTEXT_ARGUMENT` | Reserved model-visible tool argument carrying a profile-context handle in modern mode. |
| `PROFILE_CONTEXT_META_KEY` | Reserved request metadata key carrying the same handle for methods without tool arguments. |
| `createAuthenticatedRequestContextBoundary` | Derives an opaque deployment and chat binding only from claims verified by a trusted embedding host. |
| `requireAuthenticatedRequestContext` | Fails closed when a modern request has no configured trusted authentication boundary. |
| `AuthenticatedRequestContextError` | Fixed-code error class that carries no identity, provider, request, or key details. |
Expand All @@ -18,7 +23,7 @@
| `generateConfigSchema` | Generates the editor-facing JSON Schema for the configuration contract. |
| `presetConfig` | Creates a supported configuration preset in memory. |

`createMiftahRuntime` returns `MiftahRuntime`, which exposes the resolved `config`, `connect(transport)`, and `close()` methods. Supply an MCP SDK transport such as `StdioServerTransport`; transport types are provided by the direct `@modelcontextprotocol/sdk` dependency.
`createMiftahRuntime` returns `MiftahRuntime`, which exposes the resolved `config`, `connect(transport)`, and `close()` methods. Its optional `MiftahRuntimeOptions` enables the modern profile-context boundary described below. Supply an MCP SDK transport such as `StdioServerTransport`; transport types are provided by the direct `@modelcontextprotocol/sdk` dependency.

```ts
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
Expand All @@ -38,9 +43,23 @@ Claims are rejected at exact expiry. Provider failures and missing claims return

The current CLI-owned Streamable HTTP server remains the documented legacy session-aware path and does not synthesize these claims from its static bearer token. Until a supported host supplies a verified per-chat claim and the modern protocol path is enabled, deploy a profile-scoped or operator-locked endpoint and do not claim chat-scoped switching.

## Stateless profile-context handles

`ProfileContextHandleService` is the production account-selection primitive for an embedding host that has already authenticated each modern request. Pass it, together with the host's `AuthenticatedRequestContextBoundary`, through `createMiftahRuntime(configPath, { modernProfileContext })`. The configuration must enable a concrete audit journal so profile transitions cannot silently bypass their required audit commit. The host must provide its verified request result through the MCP SDK `authInfo` request field. Miftah authenticates first and derives the binding only through the configured boundary; it does not trust `clientInfo`, arbitrary headers, raw request `_meta`, tool arguments, or a model-created conversation identifier as identity.

Each handle is a short-lived AES-256-GCM bearer bound to the deployment, sealing-key epoch, existing profile, verified issuer, subject, audience, and trusted per-chat claim. All instances in a deployment must use the same atomic `ProfileContextKeyringSnapshot` and deployment-wide `ProfileContextRevocationStore`. A retained `ProfileContextKeyEpoch` may resolve old handles only during its declared overlap. The service rejects minting-key rollback and same-epoch key replacement within one process, but that memory resets on restart; the deployment key manager must enforce those rules at the source and alert on every epoch regression. Key-manager, clock, randomness, and revocation failures fail closed. The audit correlation is produced with a separate key. Never reuse the sealing key, authenticated-context binding key, or either audit key.

`InMemoryProfileContextRevocationStore` is bounded and appropriate only for tests or multiple services in one process. A round-robin or multi-process deployment must provide shared revocation storage whose successful writes are visible to every instance before the API reports completion. `ProfileContextRevocationStore.revoke` receives Miftah's validated operation time so bounded stores can prune consistently with an injected clock. The keyring provider and revocation store are trusted deployment infrastructure; the handle contains no OAuth token or upstream credential. `ProfileContextKeyringProvider` is invoked once per mint, resolve, and revoke and twice during replace, so hosts must serve a short-TTL cached atomic snapshot and enforce their own key-manager timeout.

In modern mode, account-sensitive tool schemas include the reserved model-visible `PROFILE_CONTEXT_ARGUMENT`. The first `miftah_use_profile` call may omit it and receives a new handle. A switch requires the current handle, commits a bearer-free audit transition, then revokes the old handle before returning its replacement. Tools that do not carry ordinary arguments use `PROFILE_CONTEXT_META_KEY`. Miftah strips either form before audit argument capture and before every upstream call, rejects duplicate or nested bearer placement, and records only the separately keyed correlation. Modern mode always applies strict cross-profile tool discovery, so every profile must expose identical client-visible tool names and schemas; discovery remains configuration-derived and cannot change because a prior request selected another profile.

A valid handle selects a profile; it is not operation authorization or idempotency. Policy, approval, identity, lease, OAuth, and upstream checks still run for every request. Missing, malformed, tampered, expired, revoked, cross-principal, cross-chat, cross-deployment, and removed-profile handles return fixed `ProfileContextHandleErrorCode` failures. The modern runtime never reads or mutates `ProfileManager`'s legacy active profile.

The package exports `ProfileContextHandleServiceOptions`, `ModernProfileContextRuntimeOptions`, `MintedProfileContext`, `ResolvedProfileContext`, `ProfileContextReplacementAudit`, `ProfileContextKeyringProvider`, `ProfileContextKeyringSnapshot`, `ProfileContextKeyEpoch`, and `ProfileContextRevocationStore` for host integration. The current CLI-owned Streamable HTTP entry point does not enable this option; protocol-era negotiation and transport selection remain separate work, so existing stdio and session-aware HTTP behavior stays unchanged.

## Type exports

The package root also exports `AuthenticatedRequestContext`, `AuthenticatedRequestContextBoundary`, `AuthenticatedRequestContextBoundaryOptions`, `AuthenticatedRequestContextErrorCode`, `VerifiedHttpRequestClaims`, and `VerifiedHttpRequestClaimsProvider` for the trusted host boundary. 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`.
The package root also exports `AuthenticatedRequestContext`, `AuthenticatedRequestContextBoundary`, `AuthenticatedRequestContextBoundaryOptions`, `AuthenticatedRequestContextErrorCode`, `VerifiedHttpRequestClaims`, and `VerifiedHttpRequestClaimsProvider` for the trusted host boundary. `MiftahRuntimeOptions`, `ModernProfileContextRuntimeOptions`, `MintedProfileContext`, `ResolvedProfileContext`, `ProfileContextHandleErrorCode`, `ProfileContextHandleServiceOptions`, `ProfileContextReplacementAudit`, `ProfileContextKeyEpoch`, `ProfileContextKeyringProvider`, `ProfileContextKeyringSnapshot`, and `ProfileContextRevocationStore` describe modern profile-context hosting. 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.

Expand Down
10 changes: 5 additions & 5 deletions docs/plans/2026-08-11-stateless-profile-context-decision.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Stateless Profile Context Decision

Status: Accepted for follow-up implementation; the executable model in `tests/prototypes` is non-shipping research.
Status: Accepted. Issues #376 and #377 implement the trusted authentication and production profile-context primitives; transport negotiation and the remaining protocol-era integrations stay in the follow-ups below. The executable model in `tests/prototypes` remains non-shipping research.

Issues: [#362](https://github.com/mohanagy/miftah/issues/362), [#364](https://github.com/mohanagy/miftah/issues/364), [#376](https://github.com/mohanagy/miftah/issues/376), [#377](https://github.com/mohanagy/miftah/issues/377)

Expand All @@ -22,7 +22,7 @@ The selected handle format is a short-lived, authenticated-encrypted token conta

Every production instance for one deployment must share the active sealing-key epoch and a bounded revocation backend. Resolution fails closed if required key or revocation state is unavailable. Audit records use a separate keyed correlation derived from the internal identifier; the bearer itself is never logged, exported, diagnosed, or forwarded upstream.

The non-secret envelope carries the version and key-epoch identifier so an instance can select the candidate key before opening the authenticated ciphertext. Both values, plus the deployment identifier, are authenticated as additional data. A deployment keyring has exactly one active epoch for minting and may retain explicitly configured previous epochs for resolution only. The overlap lasts no longer than the maximum handle lifetime plus bounded clock skew; after that window the previous key is removed and its remaining handles fail closed. Unknown, disabled, future, or malformed epochs are invalid. Rotation changes the active epoch atomically across instances: new handles use only the new epoch, while unexpired old handles resolve only during the declared overlap. Rollback to an older minting epoch is forbidden.
The non-secret envelope carries the version and key-epoch identifier so an instance can select the candidate key before opening the authenticated ciphertext. Both values, plus the deployment identifier, are authenticated as additional data. A deployment keyring has exactly one active epoch for minting and may retain explicitly configured previous epochs for resolution only. The overlap lasts no longer than the maximum handle lifetime plus bounded clock skew; after that window the previous key is removed and its remaining handles fail closed. Unknown, disabled, future, or malformed epochs are invalid. Rotation changes the active epoch atomically across instances: new handles use only the new epoch, while unexpired old handles resolve only during the declared overlap. `ProfileContextHandleService` remembers the highest active epoch and its key only within one process, so restarts reset that defense. The deployment key manager must reject epoch rollback and same-epoch key replacement at the source, and operators must alert on any observed epoch regression.

This keeps the one-connector, named-account experience for hosts that can provide the trusted chat binding. Until a host can do so, modern stateless mode must use an operator-locked/profile-scoped endpoint or require an explicit profile for each call. It must not claim chat-scoped switching. Legacy stdio and session-aware HTTP retain their current connection-bound behavior during the documented compatibility window.

Expand Down Expand Up @@ -95,12 +95,12 @@ The model proves:
- the encrypted handle contains neither the profile nor subject in plaintext;
- returned results and fixed errors contain only a keyed audit correlation, never the capability bearer.

The prototype does not prove production key custody, distributed-store availability, real host chat claims, packaged SDK interoperability, or schema integration. Those remain release gates.
The prototype alone does not prove production key custody, distributed-store availability, or real host chat claims. The production implementation and its real SDK integration tests now cover schema threading, two-instance handle use, chat isolation, transition revocation, deterministic tool discovery, and bearer stripping; a production host still owns trusted claim verification, key custody, and deployment-wide revocation availability.

## Implementation follow-ups

- [#376](https://github.com/mohanagy/miftah/issues/376): establish the verified issuer/subject/audience/chat binding and safe host fallback.
- [#377](https://github.com/mohanagy/miftah/issues/377): implement production sealing, key epochs, revocation, schema threading, request-scoped resolution, and legacy separation.
- [#376](https://github.com/mohanagy/miftah/issues/376): completed the verified issuer/subject/audience/chat binding and safe host fallback.
- [#377](https://github.com/mohanagy/miftah/issues/377): implements production sealing, key epochs, revocation, schema threading, request-scoped resolution, and legacy separation.
- [#363](https://github.com/mohanagy/miftah/issues/363): negotiate modern stateless and legacy session-aware protocol eras before selecting either runtime path.
- [#365](https://github.com/mohanagy/miftah/issues/365): validate standard MCP routing headers independently of profile-context resolution and make catalogs deterministic/cacheable.
- [#366](https://github.com/mohanagy/miftah/issues/366): bind MRTR confirmations and cancellation to the exact authenticated context, profile handle, and request.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
"build": "tsup",
"dev": "tsup --watch",
"test": "vitest run",
"test:core": "vitest run tests/authenticated-request-context-docs-contract.test.ts tests/authenticated-request-context.test.ts tests/config.test.ts tests/config-loader.test.ts tests/config-diagnostics.test.ts tests/config-migration.test.ts tests/config-public-contract.test.ts tests/config-runtime-parity.test.ts tests/config-schema-contract.test.ts tests/executable-resolver.test.ts tests/http-server.test.ts tests/presets.test.ts tests/profile-manager.test.ts tests/provider-routing-matchers.test.ts tests/provider-routing-matchers-docs-contract.test.ts tests/routing-context.test.ts tests/routing-policy.test.ts tests/secret-provider-availability.test.ts tests/secret-providers.test.ts tests/secrets.test.ts tests/tooling-config.test.ts tests/windows-config-acl.test.ts tests/windows-config-migration-acl-failure.test.ts tests/windows-config-migration-acl.test.ts tests/windows-secret-process-resolution.test.ts",
"test:core": "vitest run tests/authenticated-request-context-docs-contract.test.ts tests/authenticated-request-context.test.ts tests/config.test.ts tests/config-loader.test.ts tests/config-diagnostics.test.ts tests/config-migration.test.ts tests/config-public-contract.test.ts tests/config-runtime-parity.test.ts tests/config-schema-contract.test.ts tests/executable-resolver.test.ts tests/http-server.test.ts tests/presets.test.ts tests/profile-context-handle-docs-contract.test.ts tests/profile-context-handle.test.ts tests/profile-manager.test.ts tests/provider-routing-matchers.test.ts tests/provider-routing-matchers-docs-contract.test.ts tests/routing-context.test.ts tests/routing-policy.test.ts tests/secret-provider-availability.test.ts tests/secret-providers.test.ts tests/secrets.test.ts tests/stateless-profile-context-runtime.test.ts tests/tooling-config.test.ts tests/windows-config-acl.test.ts tests/windows-config-migration-acl-failure.test.ts tests/windows-config-migration-acl.test.ts tests/windows-secret-process-resolution.test.ts",
"test:oauth-console": "vitest run tests/oauth tests/remote-oauth tests/console tests/identity tests/provider-adapter-contract.test.ts tests/preset-catalog.test.ts tests/init-command.test.ts tests/cli-exit-codes.test.ts tests/cli-parse.test.ts tests/config-migration.test.ts tests/audit-integrity.test.ts",
"test:package": "vitest run tests/package-contract.test.ts",
"test:coverage": "vitest run --coverage",
Expand Down
Loading