Skip to content
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
51fae8e
feat(config): add versioned compatibility contracts
mohanagy Jul 14, 2026
7135e63
fix(windows): anchor config ACL helper
mohanagy Jul 14, 2026
d720f3e
test(windows): surface ACL probe failure class
mohanagy Jul 14, 2026
7a36e9b
test(windows): decode ACL probe diagnostics
mohanagy Jul 14, 2026
fa66af4
test(windows): classify ACL probe stage
mohanagy Jul 14, 2026
1f2607c
test(windows): classify ACL apply failures
mohanagy Jul 14, 2026
09c725b
test(windows): isolate ACL migration provider boundary
mohanagy Jul 14, 2026
20b6efa
fix(windows): use native ACL security APIs
mohanagy Jul 14, 2026
cc34000
test(windows): diagnose private ACL directory creation
mohanagy Jul 14, 2026
2c4c285
test(windows): distinguish ACL helper bootstrap
mohanagy Jul 14, 2026
57ae0d8
test(windows): isolate ACL helper preamble
mohanagy Jul 14, 2026
0db3e63
test(windows): isolate ACL request parsing
mohanagy Jul 14, 2026
5f8eb36
fix(windows): restore trusted PowerShell module path
mohanagy Jul 14, 2026
736b6a2
fix(windows): remove PowerShell module dependency
mohanagy Jul 14, 2026
9aa96ed
test(windows): diagnose ACL security copy boundary
mohanagy Jul 14, 2026
f5a1de3
test(windows): reproduce held backup ACL copy
mohanagy Jul 14, 2026
44ee156
test(windows): reproduce inherited ACL backup copy
mohanagy Jul 14, 2026
6ab7006
test(windows): classify ACL copy request framing
mohanagy Jul 14, 2026
e8b4b5b
test(windows): classify ACL copy request availability
mohanagy Jul 14, 2026
4713c21
test(windows): trace ACL copy native boundaries
mohanagy Jul 14, 2026
e1acf9c
fix(windows): verify inherited ACL copies as binary descriptors
mohanagy Jul 14, 2026
3be497e
test(windows): probe native ACL descriptor verification
mohanagy Jul 14, 2026
21c83d1
test(windows): isolate ACL copy after writer close
mohanagy Jul 14, 2026
bd67b47
test(windows): probe binary ACL descriptor transfer
mohanagy Jul 14, 2026
ae44700
fix(windows): avoid ACL descriptor reserialization
mohanagy Jul 14, 2026
44d97de
test(windows): isolate ACL control-flag transfer
mohanagy Jul 14, 2026
8419c53
test(windows): account for inherited ACL normalization
mohanagy Jul 14, 2026
3b26d25
fix(config): clarify version and redaction diagnostics
mohanagy Jul 14, 2026
5aa5001
test(windows): require ACL probe timeout cleanup
mohanagy Jul 14, 2026
02d476c
test(windows): bound ACL descriptor probes
mohanagy Jul 14, 2026
ef55083
refactor(cli): centralize migration restoration
mohanagy Jul 14, 2026
e62f910
test(windows): probe persisted ACL rule comparison
mohanagy Jul 14, 2026
39efb5e
fix(windows): verify persisted ACL access rules
mohanagy Jul 14, 2026
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ All notable changes to this project will be documented in this file. The format
- [#30](https://github.com/mohanagy/miftah/issues/30) Delivered typed opt-in GitHub, Sentry, Jira, Linear, and PostHog provider routing matchers: fixed in-tree evaluation, canonical bounded argument/URL/resource-URI signals, safe Git/package context, deterministic ambiguity, client-visible multi-upstream routing, and redacted preview/audit evidence.
- [#31](https://github.com/mohanagy/miftah/issues/31) Delivered configurable audit-journal size/age rotation and safe retention, cross-process JSONL coordination, optional SHA-256-chain tamper evidence with first-break verification, and explicit redacted support export that omits stored arguments by default.
- [#34](https://github.com/mohanagy/miftah/issues/34) Delivered the versioned local plugin API for explicit secret providers and routing matchers: strict allowlisted configuration, preflight manifest/path validation, scrubbed bounded child hosts, canonical secret references and routing signals, redaction registration, request-level cancellation/timeout containment, package contracts, and reference documentation.
- [#38](https://github.com/mohanagy/miftah/issues/38) Added configuration format v2 with an explicit dry-run-first `migrate-config` command, exact exclusive backups for opted-in writes, historical v1 compatibility fixtures, documented compatibility/removal windows, and versioned public extension, CLI, management-tool, and audit contracts.

### Changed

- [#16](https://github.com/mohanagy/miftah/issues/16) The library root export is now an intentional, documented public API. Internal server, process, profile, routing, policy, audit, and secret-management classes are no longer available from `@lubab/miftah`; use the configuration utilities and `createMiftahRuntime()` instead. This pre-1.0 breaking change requires a minor release.
- [#26](https://github.com/mohanagy/miftah/issues/26) Unmatched tool names now default to destructive risk instead of write risk. Set `tooling.unknownToolRisk: "write"` only when the compatible, less restrictive default is intentional.
- [#38](https://github.com/mohanagy/miftah/issues/38) `MiftahConfig` is now a version-discriminated union: version 2 rejects removed configuration aliases at compile time while version 1 retains its documented compatibility surface. This intentional pre-1.0 type-surface change requires consumers that previously used `interface X extends MiftahConfig` to compose with a type intersection instead.

### Fixed

Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ The default endpoint is `http://127.0.0.1:3000/mcp`; see [HTTP server transport]

Profiles are named credential environments. Keep secret values outside JSON and use the exact generated references in the checked-in [GitHub](examples/github.miftah.json), [Sentry](examples/sentry.miftah.json), or [generic reference](examples/generic.miftah.json) example. The strict catalog pins GitHub to `ghcr.io/github/github-mcp-server:v1.5.0` with its documented read-only tool configuration; it does not claim a digest. The [compatibility matrix](docs/presets-and-clients.md) describes safe promotion and deployment recording for that tag.

Claude can call `miftah_list_profiles`, `miftah_current_profile`, `miftah_use_profile`, `miftah_reset_profile`, `miftah_lock_profile`, `miftah_unlock_profile`, `miftah_profile_info`, `miftah_health`, `miftah_validate_config`, `miftah_list_upstream_tools`, `miftah_restart_profile`, `miftah_verify_identity`, and `miftah_route_preview`. Upstream tools are exposed unchanged unless they collide with a reserved management name. After a profile change, restart, upstream failure, recovery, or upstream list-change notification that changes a public capability surface, MCP clients receive list-change notifications and should re-list the affected tools, resources, resource templates, or prompts before relying on cached capabilities.
Claude can call `miftah_list_profiles`, `miftah_current_profile`, `miftah_use_profile`, `miftah_reset_profile`, `miftah_lock_profile`, `miftah_unlock_profile`, `miftah_profile_info`, `miftah_health`, `miftah_validate_config`, `miftah_list_upstream_tools`, `miftah_restart_profile`, `miftah_verify_identity`, `miftah_route_preview`, `miftah_list_approvals`, `miftah_approve`, and `miftah_deny`. In a multi-upstream bundle, upstream tools are exposed as `<upstream>__<tool>`. For a single upstream whose exact tool name collides with a reserved management name, the default is `upstream_<name>`; `tooling.collisionStrategy: "fail"` instead rejects it. Other upstream names that merely start with `miftah_` are not reserved. After a profile change, restart, upstream failure, recovery, or upstream list-change notification that changes a public capability surface, MCP clients receive list-change notifications and should re-list the affected tools, resources, resource templates, or prompts before relying on cached capabilities.

Active profile state is in-memory by default. `state.scope: "session"` resets on a new MCP transport; opt-in `workspace` or config-identity-namespaced `global` scope persists only safe selection metadata (the profile and timestamp) using atomic owner-restricted storage. Clients cannot choose a scope or state path. Optional runtime locks and risk leases are connection-bound and never enter that durable state. See [active profile state](docs/config.md#active-profile-state) for lock precedence, fallback diagnostics, and platform paths.

Expand Down Expand Up @@ -89,7 +89,7 @@ Use `"streamable-http"` for a remote MCP server:
}
```

Header names are case-insensitive, and a profile header always replaces the upstream header of the same name. Miftah requires HTTPS for non-loopback remote URLs; HTTP is accepted only for local development endpoints on `localhost`, `127.0.0.0/8`, or `::1`. The `"http"` transport is a compatibility alias for `"streamable-http"`. `"sse"` remains available for legacy MCP servers but is deprecated; prefer Streamable HTTP for new deployments.
Header names are case-insensitive, and a profile header always replaces the upstream header of the same name. Miftah requires HTTPS for non-loopback remote URLs; HTTP is accepted only for local development endpoints on `localhost`, `127.0.0.0/8`, or `::1`. The `"http"` transport is a version-1-only compatibility alias for `"streamable-http"`; version 2 requires `"streamable-http"`. `"sse"` remains available for legacy MCP servers but is deprecated; prefer Streamable HTTP for new deployments.

An intentional Streamable HTTP restart or wrapper shutdown sends the MCP session DELETE request before closing the local client transport. A server may decline DELETE with HTTP 405, in which case Miftah still closes its local session but the remote server controls any remaining server-side state. If DELETE does not settle before the configured shutdown deadline, Miftah aborts the local transport rather than leaving a live credential session behind. Requested cancellation and requested progress notifications are forwarded across both local STDIO and Streamable HTTP upstreams. HTTP status failures are returned as `UPSTREAM_HTTP_ERROR` and MCP JSON-RPC failures as `UPSTREAM_PROTOCOL_ERROR`; response bodies and remote error messages are not exposed to callers.

Expand Down Expand Up @@ -167,6 +167,7 @@ Use `miftah --help` for the generated command list and `miftah <command> --help`
| `miftah validate --config <file>` | Parse and validate JSON config; writes JSON. |
| `miftah doctor --config <file> [--json]` | Report redacted configuration and upstream readiness. |
| `miftah init [name] [--name <name>] [--preset <name>] [--output <file>] [--interactive] [--client <client>] [--credential-env <name>] [--npm-package <package>] [--docker-image <image>] [--url <url>] [--header-name <name>] [--header-prefix <prefix>]` | Generate a strict catalog template and optionally print client JSON. |
| `miftah migrate-config --config <file> [--write]` | Plan an explicit configuration-format migration; `--write` creates an exact backup before a same-directory non-overwriting publication. |
| `miftah schema` | Print the JSON Schema. |
| `miftah list-tools --config <file> [--profile <name>]` | Discover upstream tools as JSON. |
| `miftah test-profile --config <file> [--profile <name>]` | Start and initialize one profile; writes JSON. |
Expand Down
2 changes: 1 addition & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,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 (`"http"` is its compatibility alias) or the deprecated legacy SSE client. Resolved profile headers override upstream headers case-insensitively before either remote transport is constructed. 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.
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. 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.

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
11 changes: 10 additions & 1 deletion docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
Usage: miftah [command] [options]
```

The root command list is `serve`, `validate`, `doctor`, `schema`, `init`, `list-tools`, `test-profile`, `logs`, `audit-export`, `audit-verify`, and `version`. With no command, Miftah runs `serve`.
The root command list is `serve`, `validate`, `doctor`, `schema`, `init`, `migrate-config`, `list-tools`, `test-profile`, `logs`, `audit-export`, `audit-verify`, and `version`. With no command, Miftah runs `serve`.

Documented command names, options, JSON success forms, and exit categories are compatibility contracts. An incompatible CLI removal, rename, required-option change, or semantic output change requires the pre-1.0 deprecation/removal process in the [public compatibility policy](library-api.md#compatibility-policy).

`--help` and `-h` print help and exit successfully. They can appear before or after a command. Help never reads configuration, resolves secrets, or starts an upstream.

Expand All @@ -21,6 +23,7 @@ The root command list is `serve`, `validate`, `doctor`, `schema`, `init`, `list-
| `miftah doctor --config <file>` | `--config` | `--config <file>`, `--json` | Validates configuration and checks upstream readiness. Default output is a human-readable report; `--json` writes only the JSON report. A healthy or degraded report exits `0`; a failed report exits `1`. |
| `miftah schema` | none | none | Writes the Miftah JSON Schema as pretty-printed JSON. |
| `miftah init [name]` | none | `--name <name>`, `--preset <name>`, `--output <file>`, `--interactive`, `--client <claude-desktop\|claude-code\|cursor\|vscode\|all>`, `--credential-env <name>`, `--npm-package <package>`, `--docker-image <image>`, `--url <url>`, `--header-name <name>`, `--header-prefix <prefix>` | Writes a strict catalog configuration with exclusive creation and can print client JSON snippets. The positional `name` and `--name` are alternatives; the default name is `miftah-wrapper`. |
| `miftah migrate-config --config <file>` | `--config` | `--config <file>`, `--write` | Plans a supported configuration-format migration and writes a safe JSON report. It is dry-run by default. `--write` validates the candidate, makes an exact exclusive `<file>.bak`, then uses a same-directory non-overwriting publication for a changed regular non-symlink source; it never resolves secrets or starts an upstream. |
| `miftah list-tools --config <file>` | `--config` | `--config <file>`, `--profile <name>` | Starts the selected profile, discovers its upstream tools, writes a JSON array, then closes the manager. `--profile` defaults to the configured default profile. |
| `miftah test-profile --config <file>` | `--config` | `--config <file>`, `--profile <name>` | Starts and initializes one profile, writes `{"ok":true,"profile":"…"}`, then closes the manager. `--profile` defaults to the configured default profile. |
| `miftah logs --config <file>` | `--config` | `--config <file>`, `--follow` | Reads the configured audit JSONL as normalized, redacted JSONL. `--follow` continues watching it. This command does not construct an upstream manager. |
Expand Down Expand Up @@ -49,6 +52,12 @@ miftah validate --config "$HOME/Miftah configs/work wrapper.json"

`--interactive` uses a wizard only when both input and output are TTYs. EOF or Ctrl-C cancels without writing a config. It asks for variable names and safe metadata, never secret values. In noninteractive use, `init` creates only the config unless `--client` is supplied. `--client` prints JSON with absolute Node and compiled Miftah paths; it does not write a host config. Regenerate the snippet after moving or upgrading Miftah or changing the config path.

### `migrate-config`

`miftah migrate-config --config <file>` accepts only the documented supported formats and writes a JSON report containing source/target versions, safe structural actions, and whether a write occurred. It reads and validates the candidate before it changes anything. It does not emit a raw config, a diff, resolved secret values, or provider output.

`--write` is intentionally required for mutation. For a changed valid-UTF-8 configuration, Miftah refuses symlinks and non-regular sources, captures a source snapshot, moves it into a dedicated same-directory transaction directory, and privately prepares the exact backup and synced candidate. It publishes each only to an absent destination path, so it never overwrites a concurrent file. On Windows, the transaction directory is created with a current-user-only DACL and the source owner/group/DACL is copied and verified before either private file receives source-derived bytes. If publication cannot complete, Miftah restores the verified original when it can do so without overwriting anything; otherwise it exits nonzero and reports the retained recovery transaction directory. A current configuration reports `changed: false`; with `--write` it remains untouched and creates no backup. See [configuration version compatibility](config.md#configuration-version-compatibility-and-migration) for version windows and exactly which aliases can be migrated.

### `doctor`

`doctor` checks configuration, secret references, external provider availability, redaction, permissions, configured audit storage, executable availability, upstream startup, discovery, and clean shutdown where applicable. Its checks have stable `code`, `status`, `target`, `explanation`, and `remediation` fields.
Expand Down
Loading
Loading