feat(apps): Vanta connection for their MCP server - #1
Open
marcorivm wants to merge 6 commits into
Open
Conversation
Reconciliation Stage A. getCurrentPlan() reports enterprise; the OSS policy validator wiring and now-empty policy-flags/policy-oss-locks modules are removed (the coherence bridge was already dropped upstream); lock affordances are edition-neutral (UnavailableBadge, informational ProAppDialog); the OneCLI Cloud promo blocks and cloud_only error string are removed. No agent-group code, no migration.
# Conflicts: # apps/web/src/lib/init/api.ts
Reconciliation Stage C. /v1/org/groups CRUD + replace-set membership (org-membership validated on the global-FK userId), /groups as a single user-groups view. Agent groups and the org-agent directory are dropped (orphaned once agent groups are gone). No orphan-neutralization pass: upstream's grants engine treats an FK-orphaned rule identity as inert, so a group delete cannot widen a rule. +59 tests, no agent-group code, no migration.
Reconciliation Stage D. /v1/projects (rename, access bindings replace-set, safe delete with pinned cascade and last-owner/stranding guards) plus /settings/project UI; /v1/org/role-mappings (CRUD, ordering, preview) with monotonic raise-only apply re-wired into the group membership writers via the applyRoleMappingsForGroup seam Stage C had stripped. Role changes audit under MEMBER, config under ROLE_MAPPING. +136 tests, no agent-group code, no migration. (Role-mappings management UI folds into Stage E.)
…44.0 Reconciliation Stage F. The OSS gateway now populates the org rule set and the user/group PrincipalSet that upstream shipped but never filled: a new loaders.rs adds the org published-rule loader and a principal CTE mirroring the API's resolvePrincipalSet (users direct and via granted groups, active members only; groups direct and inherited; fully org-fenced, agent-groups dropped). Two-level evaluation mirrors upstream's own evaluator including the hard-floor rule (a lone allow at one level cannot open the other level's default block), so an org guardrail can't be bypassed by a project allow. Fail-closed via upstream's anyhow refuse-CONNECT; our old org_degraded/Fallback/kill-switch scaffolding is deleted. Empty org fails OPEN. +21 tests, no agent-group, no signature changes to the call sites.
Vanta's MCP server authorizes as an MCP resource, not through the REST API: its metadata advertises only authorization_code + refresh_token with token_endpoint_auth_methods_supported: ["none"], S256 PKCE, a dynamic registration endpoint, and its own scope namespace (mcp-api.*). There is no client-credentials shortcut and no secret an operator could configure, so BYOC (`configurable`) has nothing to hold — hence two new pieces of generic OAuth plumbing alongside the app itself. - PKCE (RFC 7636): oauth methods can set `pkce: true`; the connect route mints the pair, sends the challenge, and keeps the verifier in an httpOnly cookie scoped to the callback path — never in the state that travels to the provider. - Dynamic client registration (RFC 7591): `dynamicRegistration` apps get a public client minted on demand, cached in the project's AppConfig row keyed by (region, redirectUri) so a moved deployment origin re-registers. The callback never registers: the code is bound to the client that started the flow. Minting is audited as an app-config write. - Gateway: `vanta` over the three regional MCP hosts, each gated by `credential_host_field: "mcp_host"` so a token minted for one region can never be injected into another's host. api.vanta.com is deliberately absent — these tokens are MCP-scoped. Refresh runs through the existing refresh_token path (which persists rotated tokens) via a new PublicClient auth mode plus credential-sourced client id and token endpoint, since both are per-connection for a registered client. Regions: US by default; eu/aus resolve end to end from `?region=` on the connect URL, with no picker in the dashboard yet. No app-permissions catalog entry: every MCP call is POST /mcp, so the host/path/method tool model cannot separate reads from writes and an entry would author misleading "all reads" rules. Policy targets Vanta as a network host for now. Verified live: registration is accepted and returns a public client id; the authorize URL built from it is accepted with no parameter rejection; the token endpoint recognizes the client. The consent leg and first token exchange need a Vanta Admin login and are untested.
marcorivm
force-pushed
the
open-edition/reconciled
branch
from
August 8, 2026 18:30
36aebad to
caaf574
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a Vanta connection so agents can use Vanta's MCP server through the gateway — connect once in the dashboard, then point any MCP client at
https://mcp.vanta.com/mcpwith no auth and let the gateway inject credentials.Why this needed new OAuth plumbing
Vanta's MCP server authorizes as an MCP resource, not through Vanta's REST API. Its live metadata:
So Vanta can't be a client-credentials import (MongoDB Atlas shape), and it can't use BYOC — there is no secret for an operator to bring, so
configurablehas nothing to hold. Two generic pieces were added alongside the app:PKCE (RFC 7636) — an oauth method can set
pkce: true. The connect route mints the pair, sends the challenge, and keeps the verifier in an httpOnly cookie scoped to the callback path — deliberately not in the signed state, which travels to the provider.Dynamic client registration (RFC 7591) — a
dynamicRegistrationapp gets a public client minted on demand, cached in the project's AppConfig row keyed by(region, redirectUri), so a moved deployment origin re-registers (a client id is only valid for its registered redirect URI). The callback leg never registers: the authorization code is bound to the client that started the flow, so a fresh client there could only produce a failed exchange. Minting writes config, so it emits anapp-config/createaudit event.Gateway
vantais registered over the three regional MCP hosts, each gated bycredential_host_field: "mcp_host", so a token minted for one region can never be injected into another region's host.api.vanta.comis deliberately not registered — these are MCP-scoped tokens the REST API does not accept.Refresh goes through the existing
refresh_tokenpath (the one that persists rotated refresh tokens) rather thantry_refresh_credentials, which cannot return a rotated token. That needed a newClientCredentialMethod::PublicClient(client_id in body, no secret anywhere) plus credential-sourced client id and token endpoint, since for a registered client both are per-connection rather than per-deployment.RefreshConfiggained two optional fields — EE builds defining their ownRefreshConfigliterals will need them added.Verified
Against Vanta's live endpoints, without a Vanta account:
client_idwith no secret.mcp-api.*scopes, RFC 8707resourceall accepted).Locally:
pnpm checkclean; API and gateway suites pass (20 new tests covering region→endpoint mapping, credential shape, PKCE enforcement, registration caching/re-registration, and the never-register-on-callback rule).Not verified: the consent leg, the first real token exchange, and refresh — these need a Vanta Admin login. Worth exercising before merge.
Known limits (deliberate)
eu/ausresolve end to end from?region=on the connect URL, but there's no picker in the dashboard yet, so an EU/AUS tenant can't self-serve.POST /mcp, so the host/path/method tool model cannot separate reads from writes — an entry would author misleading "all reads" rules. Policy targets Vanta as a network host (mcp.vanta.com) for now; per-tool rules would need body conditions, whose matching is EE-only.tryHandleOrgAuthorize) runs before this plumbing, so org-scoped Vanta connections aren't supported yet.Note
Verification created one anonymous, un-consented client registration at Vanta (
vci_a69fa253…, redirect URIhttps://api.onecli.sh/v1/apps/vanta/callback). Vanta returned no registration access token, so RFC 7592 self-deletion isn't available — only Vanta support can remove it. It grants nothing until an admin authorizes it.