# AWS KMS Guard gateway v1

This kit is for teams that already sign Base USDC transactions with an AWS KMS
`ECC_SECG_P256K1` key and cannot move that key into a managed wallet. It keeps
the key in the customer's AWS account and makes the guarded gateway the only
principal allowed to call `kms:Sign`.

Public, versioned files:

- `https://tollary.p-e.kr/sdk/aws-kms-guard-gateway-1.0.0.mjs`
- `https://tollary.p-e.kr/sdk/aws-kms-guard-gateway.manifest.json`
- `https://tollary.p-e.kr/sdk/aws-kms-fit-check-1.0.0.mjs`
- `https://tollary.p-e.kr/sdk/aws-kms-iam/agent-role-policy.json`
- `https://tollary.p-e.kr/sdk/aws-kms-iam/gateway-role-policy.json`
- `https://tollary.p-e.kr/sdk/aws-kms-iam/kms-key-policy-fragment.json`

Pin the versioned module and its SHA-256 from the manifest. The modules are
self-contained Node 24 ESM files; no npm dependency or AWS credential is
embedded.

## Ten-minute offline fit check

Download the fit-check and all three IAM templates into the same directory
layout, then run it before creating any AWS resource:

```sh
mkdir -p pou-kms-fit/examples/aws-kms-iam
cd pou-kms-fit
curl -fsSLo aws-kms-fit-check-1.0.0.mjs \
  https://tollary.p-e.kr/sdk/aws-kms-fit-check-1.0.0.mjs
curl -fsSLo examples/aws-kms-iam/agent-role-policy.json \
  https://tollary.p-e.kr/sdk/aws-kms-iam/agent-role-policy.json
curl -fsSLo examples/aws-kms-iam/gateway-role-policy.json \
  https://tollary.p-e.kr/sdk/aws-kms-iam/gateway-role-policy.json
curl -fsSLo examples/aws-kms-iam/kms-key-policy-fragment.json \
  https://tollary.p-e.kr/sdk/aws-kms-iam/kms-key-policy-fragment.json
node aws-kms-fit-check-1.0.0.mjs --json
```

The command is deliberately offline. It contacts neither AWS, an RPC, the
Guard service, nor a wallet. Exit code `0` and `"ready":true` prove only that
the downloaded gateway rejects recipient, amount, proof, and signer mutations
in generated fixtures and that the templates preserve the intended role
separation. They do not prove the policies deployed in your AWS account.

## Required production boundary

1. The agent role has an explicit `Deny` for `kms:Sign`.
2. Only a distinct gateway role can invoke `kms:Sign` on one immutable key ARN.
3. The agent can call only the gateway's narrow `guarded-sign` operation.
4. Every alternate signer, wallet, Lambda, role-assumption path, raw relay, and
   broadcast route is removed from the agent.
5. The mandate issuer is controlled separately from the KMS transaction sender.
6. The Guard attestation descriptor and mandate issuer are pinned through an
   independently authenticated configuration path.

If any alternate signing route remains, the integration is advisory and should
not be described as enforced.

## Connect the customer-owned AWS SDK

The public module never imports AWS credentials. The customer-owned gateway
adapts its existing AWS SDK client into one digest-only callback:

```js
import { KMSClient, SignCommand } from '@aws-sdk/client-kms';
import {
  createAwsKmsSecp256k1GuardGateway
} from './aws-kms-guard-gateway-1.0.0.mjs';

const kms = new KMSClient({ region: 'ap-northeast-2' });

const gateway = createAwsKmsSecp256k1GuardGateway({
  keyId: process.env.KMS_KEY_ARN,       // full immutable ARN, never an alias
  expectedSender,                      // pinned outside the agent request
  expectedIssuer,                      // distinct owner/control-plane pin
  publicDescriptor,                    // pinned Guard Ed25519 descriptor

  kmsSignDigest: async input => {
    const result = await kms.send(new SignCommand(input));
    return {
      KeyId: result.KeyId,
      SigningAlgorithm: result.SigningAlgorithm,
      Signature: new Uint8Array(result.Signature)
    };
  },

  // This capability must also be reachable only from the gateway role.
  broadcast: ({ serializedTransaction }) =>
    restrictedBaseTransport.broadcast({ serializedTransaction })
});

// The returned object is frozen and exposes only execute().
await gateway.execute({ proof, attestationHeader, transaction });
```

The gateway verifies the signed Guard proof and every unsigned transaction
field before KMS, normalizes the AWS DER ECDSA signature to Ethereum low-s,
recovers the configured sender from the exact signed bytes, rechecks freshness,
and only then invokes the customer-owned broadcaster. A raw key, mnemonic, AWS
credential, generic signing method, or broadcast escape hatch is never accepted
or exposed.

Start on Base Sepolia with an unfunded or test-only sender. Mainnet activation
and real USDC are separate operator decisions and are not enabled by this kit.
