# JSON General serialization (multi-signature) _(no submodules)_ ## Purpose RFC 7515 §3.2 + §7.2.1 JSON-general JWS — `{ payload, signatures: JwsSignature[] }`. Each entry in `signatures` carries its own `protected` / `header` / `signature` so the same payload can be co-signed by N parties. Common use cases: cross-tenant message attestation, multi-party authorisation, signed installer manifests where multiple maintainers' signatures must all attach to the same artifact. ## Features - `generalDecode` — Zod-validated decode; `signatures` must be non-empty. - `generalVerify` — verifies every signer via a per-signer key resolver; returns `{ verifiedIndices, failedIndices }`. - `generalSign` — signs the same payload under N signers; rejects mixed-`b64` signers. - `requireAll: true` causes the first failing index to throw `JWS.MULTI_SIG_FAILED` with `error.context.failedIndex`. ## Usage: Basic Example ```ts import { generalSign, generalVerify } from "@suaveplan/jws"; const general = await generalSign("hello", [ { key: secret, alg: "HS256" }, { key: rsaPriv, alg: "RS256" }, ]); const result = await generalVerify( general, async (i) => (i === 0 ? secret : rsaPub), { algorithms: ["HS256", "RS256"] } ); // result.verifiedIndices === [0, 1] ``` ## Usage: Advanced Example ```ts import { generalVerify, JwsError } from "@suaveplan/jws"; try { await generalVerify( general, async (i, protectedHdr) => keyStoreByIssuer.get(protectedHdr["iss"] as string), { algorithms: ["HS256", "RS256", "ES256"], requireAll: true, } ); } catch (err) { if (err instanceof JwsError && err.code === "JWS.MULTI_SIG_FAILED") { const failedIndex = (err.context as { failedIndex: number }).failedIndex; // err.cause === underlying JwsError for that signer. } } ``` ## API Reference - `generalDecode(json: unknown): GeneralJws` - `generalVerify(json: unknown, keyResolver, opts: GeneralVerifyOptions): Promise` - `keyResolver: (signerIndex, protectedHeader, unprotectedHeader?) => Promise` - `GeneralVerifyOptions extends VerifyOptions { requireAll?: boolean }` - `GeneralVerifyResult = { verifiedIndices, failedIndices, payloadString }` - `generalSign(payload, signers: GeneralSigner[]): Promise` - `GeneralSigner = { key, alg, protectedHeader?, unprotectedHeader? }` ## Implementation Notes The default `requireAll` value is `false` — more flexible for use cases like "any one of N trust roots verifies." Callers needing the stricter "every signer must verify" semantics opt in via `requireAll: true`. Mixed `b64` settings across signers are NOT supported because each signer's signing input would differ in interpretation and the receiver-side verifier cannot know which interpretation to apply — `generalSign` rejects mixed-`b64` arrangements with `JWS.MALFORMED`. The payload segment is computed from the first signer's b64 setting and then asserted byte-identical across the rest.