Skip to main content
[AVAILABLE]. packages/execution-control/src/GatewayAttestation.ts.

What it is

Every time the gateway releases an execution through execution-control, it mints a fresh, signed attestation proving it possesses the gateway’s private key, for this specific request:
// packages/execution-control/src/GatewayAttestation.ts
export interface GatewayAttestationPayload {
  readonly gatewayId: string;
  readonly authorizationId: string;
  readonly nonce: string;
  readonly issuedAt: string;
}

Why it exists

The signed execution authorization proves Parmana’s policy engine approved an action. It does not, by itself, prove that this specific gateway instance, at this specific moment, is the one releasing it to a connector. A connector that trusts an authorization without also checking who’s handing it over has no way to distinguish a legitimate release from anything else that got hold of a valid envelope. Gateway attestation is that second, separate check.

How it behaves

// packages/execution-control/src/GatewayAttestation.ts:50-71
export class GatewayAttestationSigner {
  constructor(private readonly clock: Clock, private readonly idGenerator: IdGenerator) {}

  sign(gatewayId: string, authorizationId: string, privateKey: KeyObject): GatewayAttestation {
    const payload = Object.freeze({
      gatewayId,
      authorizationId,
      nonce: this.idGenerator.generate(),
      issuedAt: this.clock.now().toISOString(),
    });
    const signature = sign(null, serializer.serialize(payload), privateKey).toString("base64");
    return Object.freeze({ payload, signature });
  }
}
authorizationId binds the attestation to one specific request, so it cannot be replayed to authenticate a different request. The default server mints one of these per release, using its own dedicated keypair (keys/gateway.*.pem), deliberately separate from the authorization-verification key: see Deploy patterns for why the keys are kept apart.

Minimal example

const attestationSigner = new GatewayAttestationSigner(new SystemClock(), new RandomIdGenerator());
const attestation = attestationSigner.sign(gatewayId, authorizationId, gatewayPrivateKey);
// { payload: { gatewayId, authorizationId, nonce, issuedAt }, signature }

const valid = verifyGatewayAttestationSignature(attestation, gatewayPublicKey);

What it does not prove

This is the honest-scope note from the source code’s own doc comment (GatewayAttestation.ts:10-29), not a summary of it: “nonce is an entropy/uniqueness field only, nothing in this file or in SignedTokenConnectorAuthenticator records consumed nonces, so it is not itself a replay defense. It exists so two attestations minted for the same authorizationId (e.g. a retried call) are distinguishable, and so the signed payload isn’t otherwise fully deterministic from (gatewayId, authorizationId, issuedAt) alone. Replay of the SAME attestation against the SAME request is prevented elsewhere: the single-use GatewaySession (InMemoryGatewaySessionStore) rejects a second execution attempt once a session has been consumed, and replay of the underlying SignedExecutionAuthorization itself is the responsibility of @parmana/envelope-verifier’s NonceStore, upstream of everything in this package. If this attestation’s nonce needs to independently defend against replay in the future, a consumer would need to track seen (gatewayId, nonce) pairs, nothing here does.”
In short: gateway attestation proves who released a request and binds that proof to which request, one signature answering one question. It relies on other, separate mechanisms (session consumption, the envelope’s own nonce store) for replay protection, it doesn’t duplicate them.

Next

Credential isolation

What happens after a gateway attestation is accepted: credential issuance.

Determinism and clocks

The injected Clock and IdGenerator this attestation signer depends on.