> ## Documentation Index
> Fetch the complete documentation index at: https://docs.parmanasystems.com/llms.txt
> Use this file to discover all available pages before exploring further.

# The Gateway

> The sole boundary through which Parmana releases approved execution requests to connectors. Wired into the default server unconditionally, as of commit 651497a.

<Info>**\[AVAILABLE]**. `packages/execution-gateway`, 25 tests.</Info>

<Warning>
  **This page corrects a claim this site made through 2026-07-08**: earlier material says the
  gateway is "a library you wire in, not a default," and that a stock `packages/api` server
  gets no content-binding enforcement. That was true then. As of commit `651497a`
  (2026-07-11), `packages/api/src/bootstrap/createExecutionSystem.ts` unconditionally returns
  `createExecutionGateway()`, there is no server code path that skips it. See
  [Quickstart](/quickstart) for a verified live run.
</Warning>

## What it is

`ExecutionGateway` is the sole boundary through which a [signed execution
authorization](/concepts/execution-authorization) actually results in something running. It
does not trust the authorization it's handed: it independently re-verifies the envelope and
re-derives the content hash itself before anything reaches a connector.

## Why it exists

An authorization envelope proves that Parmana *decided* to approve something. It doesn't by
itself stop a receiving system from executing something else under that approval, or from
skipping verification altogether. The gateway is the one place designed to say no even if
every earlier stage said yes: the last point before a real system state changes.

## How it behaves

`ExecutionGateway` implements `@parmana/execution-system`'s `ExecutionSystem` interface, so
it plugs into `RuntimeFactory.create()` exactly like any other execution system, no change
to `RuntimeEngine` or `RuntimePipeline` is required to use it. It composes
`@parmana/envelope-verifier`'s checks (version, signature, expiry, TTL, nonce) and adds
exactly one more: recomputing the executable content hash and comparing it to
`businessTransactionHash`.

```typescript theme={null}
// packages/execution-gateway/src/ExecutionGateway.ts:150-165
if (passed) {
  const actualHash = await this.contentHasher.hash(executableContent);
  const expectedHash = request.authorization.payload.businessTransactionHash;

  businessTransactionHashMatches = actualHash === expectedHash;

  if (!businessTransactionHashMatches) {
    hashMismatch = { expected: expectedHash, actual: actualHash };
  }
}
```

Check order, side-effect-free checks first (`ExecutionGateway.ts:86-90`):

```
version → signature → expiry → TTL policy → businessTransactionHash recompute-and-compare → nonce
```

The nonce (single-use) check runs **last**, and only if every prior check, including the
content hash, passed. This matters: a forged or mismatched request must never burn a nonce,
or an attacker who observes a nonce in transit could poison it and get the *legitimate*
request rejected instead (`ExecutionGateway.ts:170-180`).

`ExecutableContentHasher` delegates to the same `TrustRecordHasher` used elsewhere in the
system (`packages/crypto/src/ExecutableContentHasher.ts`), the signing side and verifying
side run identical canonical serialization and hashing, never two parallel implementations
of the same computation.

<Note>
  **Nonce single-use is scoped to whichever `NonceStore` instance checks it.** Multiple
  independent gateway instances each using their own `MemoryNonceStore` can each accept the
  same authorization once. Fleet-wide single-use requires one shared store (CLAIMS.md 3.2).
</Note>

## Minimal example

```typescript theme={null}
const gateway = new ExecutionGateway({
  publicKey,          // Parmana's public key
  nonceStore,          // shared across instances for fleet-wide single-use
  connector,           // e.g. new HttpConnector({ baseUrl, headers })
  // or: executionControl: { service: executionControlService, route }
});

const application = createApplication(gateway); // wires it into RuntimeFactory.create()
```

The default server's own bootstrap (`packages/api/src/bootstrap/createExecutionGateway.ts`)
follows this exact shape, using `executionControl` rather than a direct `connector`, see
[Credential isolation](/concepts/credential-isolation).

## Connectors: what's actually wired today

A `Connector` is what the gateway hands verified, frozen content to after every check
passes. `HttpConnector` and `MockConnector` are reference implementations in
`@parmana/connector-sdk`. Two real, production-registered connectors exist:
[`razorpay`](/integrations/razorpay) (whenever `RAZORPAY_KEY_ID`/`RAZORPAY_KEY_SECRET` are
configured) and [`hubspot`](/integrations/hubspot) (whenever `HUBSPOT_PRIVATE_APP_TOKEN` is
configured), each making genuine external API calls, not scripted responses. Four
enterprise-named mocks also exist in the codebase
(`packages/connector-sdk/src/connectors/{sap,oracle,workday,salesforce}/`), each an explicit
`MockConnector` wrapper, self-documented as *"deterministic, in-memory, used until the real
enterprise connector is implemented."* These are reference mocks, not integrations, are
never registered by `packages/api/src/bootstrap`, and should not be represented as SAP,
Oracle, Workday, or Salesforce connectivity.

<Warning>
  **Update (Phase 2A), historical record, superseded below:** `vendor-payment`
  (`packages/api/src/bootstrap/createVendorPaymentConnector.ts`) previously registered a
  `MockConnector` unconditionally, including in production — see
  [the production certification and fix](/../architecture/phase2a-production-connectors.md)
  for why that was a trust gap. Phase 2A made it register only when `NODE_ENV=test`; **in
  production, no connector backed `payments:execute`** from that point on, and it failed
  closed with the same `"No connector registered for capability"` error any other
  unregistered action gets.

  **Final update (2026-08-09):** `payments:execute`/vendor-payment was never on the product
  roadmap as a real capability, so rather than building the independent signal verification
  that would have been required to keep it, it was removed from the repository entirely —
  `createVendorPaymentConnector.ts` no longer exists, in any form, in any environment. See
  `docs/VERIFICATION-GAPS.md` G-27 in the source repository. A generic, `NODE_ENV=test`-only
  connector (`createTestFixtureConnector.ts`, capability `test:fixture-execute`) now serves
  the narrow role of giving this repository's own shared test fixtures something to execute
  against; it is internal test scaffolding, not a product feature, and is not documented
  further here.

  Adding a real connector to the running server is a bootstrap code change today
  (`createConnectorRegistry.ts`, `createConnectorAuthenticator.ts`), not configuration. See
  [Add a connector with the Connector SDK](/integrations/connector-development-guide).
</Warning>

## Failure reporting

`ExecutionGateway.execute()` throws on any failed check, naming every failing check and, on
a content mismatch, both hashes:

```
Execution Gateway rejected request: failed checks [businessTransactionHashMatches].
businessTransactionHash mismatch: expected <hash>, got <hash>.
```

(`ExecutionGateway.ts:250-265`, `describeFailure`.)

## Next

<CardGroup cols={2}>
  <Card title="Gateway attestation" icon="stamp" href="/concepts/gateway-attestation">
    The gateway's own signature: what it proves about a release, and what it doesn't.
  </Card>

  <Card title="Credential isolation" icon="key" href="/concepts/credential-isolation">
    What happens to the credential a connector needs, once the gateway releases execution.
  </Card>
</CardGroup>
