Guides

Proof of Agency

A Proof of Agency is a compact ES256 JWS issued only for allow in enforce mode. In the current product, enforce mode is available only in sandbox. Shadow and test decisions never carry a proof.

Signed claims

JSON
{
  "iss": "https://app.graypass.org",
  "ver": 1,
  "jti": "poa_...",
  "env": "sandbox",
  "eid": "env_...",
  "tid": "tenant_...",
  "sub": "sub_...",
  "act": "sub_...",
  "act_t": "human",
  "action": "payout.destination.change",
  "res": { "type": "merchant_account", "id": "<tenant-scoped digest>" },
  "ah": "<canonical action hash>",
  "pol": "payout.destination.change@1",
  "asr": "<assurance-profile digest>",
  "aud": "payments-app",
  "iat": 0,
  "nbf": 0,
  "exp": 0,
  "apr": "sandbox_confirm",
  "mode": "enforce"
}

tid and eid prevent proof confusion between tenants or between a tenant’s sandbox and live environment. ah binds the exact environment, action, resource, context, subject, actor, actor type, audience, and nonce. The resource id itself is replaced by a tenant-scoped digest.

Agent-shaped proofs are rejected because no agent-mandate verifier exists.

Offline verification

Configure the tenant and environment independently. Do not copy expected values out of the proof you are trying to verify.

TypeScript
const result = await graypass.verify(proof, {
  audience: 'payments-app',
  expectedAction: 'payout.destination.change',
  expectedActionHash: decision.verification.actionHash,
});

if (!result.valid) throw new Error(result.reason);

Both verifiers require:

The cross-language test includes a separate jose library as a signature interoperability oracle. AWS KMS returns DER-encoded ECDSA signatures; the signer converts them to the fixed-width JOSE representation before building the compact JWS.

Canonical action hash

Python uses the Trail of Bits rfc8785 package and TypeScript uses the canonicalize RFC 8785 package. GrayPass applies a narrower profile:

Use strings for monetary decimal values.

Revocation and one-time consumption

Offline signature validity and online state are different:

The online status endpoint requires a server credential. A foreign tenant sees unknown, not an existence oracle.

Key separation and rotation

Production and staging accept KMS signers only. Both live and sandbox KMS keys are required, must differ, and are self-tested at boot. Local PEM and ephemeral keys are development-only.

JWKS may publish configured previous KMS public keys during an overlap window so unexpired proofs continue to verify. Operators must retain previous key ids for at least the verifier cache window plus the maximum proof lifetime and clock tolerance.

The signing code path and the DER-to-JOSE conversion are covered by the repository's tests. Signing against a real AWS KMS key, including IAM permissions, key policy, and region, is verified separately by an operator-run canary before any live release.

Generated from the API contract in contracts/ and the source in docs/. The build fails if this page disagrees with the running API.