Skip to main content

What it is

A connector is the piece of code that actually performs a real-world action, a HubSpot deal update, a GitHub pull request merge, a Paytm refund, a Slack message, after every governance check upstream (policy evaluation, signal verification, capability binding, execution authorization) has already approved it. Connectors are deliberately dumb: they validate requests, execute one already-authorized operation using an already-resolved credential (Chapter 11), and return a deterministic response. They never evaluate policy, never authorize anything, and never resolve credentials themselves.

Why it was built

Every governed action this system can take needs an actual integration with the outside world, and that integration needs to be swappable, testable in isolation, and structurally incapable of bypassing the governance layers above it. The Connector contract (packages/connector-sdk/src/ConnectorTypes.ts:157) exists to enforce that boundary in code, not just in documentation. Its doc comment states that “a Connector never receives a request directly from the Runtime or AI, only from SdkConnectorExecutor” (ConnectorTypes.ts:152-155), which in turn is reachable only through the Execution Gateway.

How it works

The Connector interface itself is small:
ConnectorExecutionContext (ConnectorTypes.ts:85-97) carries a credential (a CredentialHandle, already resolved by the session credential vault from Chapter 11, not a raw secret the connector has to go fetch), a timeoutMs, and a requestedAt timestamp. It also has an optional release field, which the Execution Gateway sets on every governed release. It names the authorization, the policy identity, and the signed human approvals behind the release. A connector that forwards the action outside Parmana signs it into that release (ADR-0013). Every other connector may ignore it. Each real connector in this codebase splits into two files: a *-Capabilities.ts file in a dedicated @parmana/connector-{name} package (pure metadata: capability identifier constants and request/response DTOs, no execution logic at all), and an executable adapter under packages/execution-gateway/src/connector-execution/Gateway{Name}Adapter.ts that actually performs the HTTP call. Each capabilities file says so explicitly in its own header comment (for example HubSpotCapabilities.ts and GitHubCapabilities.ts).

The four live connectors

The policy column comes from the canonical binding table in packages/capability-registry/src/CapabilityPolicyBinding.ts, the same table Chapter 6 covers. That file’s own comments say the version in effect is the one most recently approved through policy governance, so a new policy version takes effect when it is approved. Every policy in this table needs a signed human approval for the action and its resource, reads included (Human approval). HubSpot (packages/connector-hubspot/src/): two capabilities, fetch and update a deal. HubSpotSignalStateVerifier (a separate file in the same package) checks caller-declared signals against HubSpot’s own API, rather than trusting the caller’s assertion. This is the concrete example Chapter 5 (signal-state verification) points to. GitHub (packages/connector-github/src/): fetch a PR’s state, merge a PR. It authenticates as a GitHub App. GitHubAppJwt.ts in this package signs the app JWT, and GitHubAppCredentialProvider.ts (in execution-gateway) exchanges it for an installation token. The connector does not use a static personal access token. Paytm (packages/connector-paytm/src/): exactly one capability, paytm:refund, by design. Its own doc comment says this is deliberate: “There is no paytm:* wildcard and no additional Paytm capability (charge, payout, settlement query, …) declared here; adding one is a deliberate, separate milestone, not an incidental side effect of this one” (PaytmCapabilities.ts:9-14). It is structurally different from the other three in one respect. Its baseUrl is required (PaytmCapabilities.ts:43), and requests go to a separate service, the Paytm agent, which the connector calls over HTTP. The wire-level action string that this service expects, "paytm-refund" (PAYTM_AGENT_WIRE_ACTION, PaytmCapabilities.ts:29), is a transport-boundary translation only. Parmana’s own capability identity stays "paytm:refund" everywhere else. Slack (packages/connector-slack/src/): exactly one capability, slack:post-message. Its header comment applies the same “no wildcard, deliberate scope” rule as Paytm (SlackCapabilities.ts:8-13).

Razorpay: a historical case study

A Razorpay connector (RazorpayConnector, RazorpayRefundService, RazorpaySignalStateVerifier, RazorpayDailyRefundLedger, RazorpayCumulativeRefundLedger, RazorpaySettlementProcessor, and supporting files) was built, ran in production, and was removed from this codebase on 2026-08-12, in commit d8a6ded, as recorded in docs/VERIFICATION-GAPS.md. No Razorpay connector source exists on main today. Three database tables for it (razorpay_webhook_events, razorpay_webhook_audit_events, razorpay_daily_refund_reservations) outlived the code. They were dropped by supabase/migrations/20260916120000_drop_razorpay_tables.sql on 2026-09-16, which describes them as orphaned after the connector’s removal. The practical lesson: entries in docs/VERIFICATION-GAPS.md that predate 2026-08-12 and describe Razorpay-specific mechanisms as wired into production are accurate history, not current fact. The document carries a stale-narrative notice, added 2026-08-24, that says so.

How to validate this yourself

  • packages/connector-sdk/src/ConnectorTypes.ts, the Connector contract itself.
  • packages/connector-hubspot/src/, connector-github/src/, connector-paytm/src/, and connector-slack/src/, each connector’s capability metadata and DTOs.
  • packages/execution-gateway/src/connector-execution/Gateway{HubSpot,GitHub,Paytm,Slack}Adapter.ts, the executable adapters.
  • packages/capability-registry/src/CapabilityPolicyBinding.ts, the canonical capability-to-policy table.
  • packages/api/src/bootstrap/createConnectorRegistry.ts, where connectors are registered at startup and where each missing connector logs its *_connector_unavailable event. assertConnectorCapabilitiesBound.ts in the same directory checks that every declared capability has a canonical policy binding.
  • Integration tests: packages/api/tests/integration/hubspot-deal-update.integration.test.ts, github-pr-merge.integration.test.ts, and paytm-refund.integration.test.ts. Each one is a connector’s HTTP-boundary proof. Hermetic mock servers stand in for vendor APIs: MockHubSpotServer.ts (connector-hubspot), MockGitHubServer.ts (connector-github), MockPaytmConnectorServer.ts (connector-paytm), and MockSlackServer.ts (connector-slack).

Integration requirements

Each connector needs its own credentials configured, only when it is used:
  • HubSpot: HUBSPOT_PRIVATE_APP_TOKEN.
  • GitHub: GITHUB_APP_ID, GITHUB_INSTALLATION_ID, and GITHUB_APP_PRIVATE_KEY.
  • Paytm: a baseUrl pointing at a deployed Paytm agent instance. It is required, with no default.
  • Slack: see createSlackConnector.ts for the exact variable.
A connector with missing configuration is not registered at startup. It is not registered in a broken state either. createConnectorRegistry.ts logs an *_connector_unavailable event for it (hubspot_connector_unavailable, github_connector_unavailable, paytm_connector_unavailable, slack_connector_unavailable), naming the missing configuration.