@charter-agreement-protocol/signer 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,44 @@
1
+ # Changelog
2
+
3
+ All notable public changes to `@charter-agreement-protocol/signer` are
4
+ documented here.
5
+
6
+ ## [0.2.1] — 2026-09-24
7
+
8
+ Dependency-line release: this package now tracks the published verify
9
+ line - `@charter-agreement-protocol/verifier` `^0.5.0` (resolved 0.5.0).
10
+ No API change: verifier 0.5.0 added consumer-side surface only (the
11
+ capability probe, the signature registry identity, the descriptor
12
+ timestamp floor, the 104-case corpus); the producer surface this package
13
+ delegates to is unchanged. Typecheck strict, 18/18 tests against the
14
+ resolved 0.5.0. The producer-side capability alignment (mirrored
15
+ `capabilities()` with refuse-before-mint, declared minting metadata,
16
+ profile-bound signing) is the next feature release on this line.
17
+
18
+ ## [0.2.0] — 2026-09-17
19
+
20
+ ### Changed
21
+
22
+ - **The R1–R3 honest-signer refusals now run pre-sign** (previously
23
+ deferred to post-sign verification with mislabeled errors; the `refused`
24
+ error was unreachable): `signAcceptance` / `signTermination` run the
25
+ verifier package's refusal checks after framing, before the key signs.
26
+ Breaking within 0.x: `signTermination`'s view requires `chain`,
27
+ `signAcceptance` enforces the guards over the `chain` it previously
28
+ ignored, and `signReceipt` requires its issuing view (the reference
29
+ takes a context, never none).
30
+ - Post-sign verification failures return the bare `verification_failed`
31
+ (the reference vocabulary); malformed claims are rejected pre-sign as
32
+ `invalid_input` through the producer claims gate — no claim-shape defect
33
+ burns a key operation.
34
+ - **All protocol logic is delegated to the verifier package's producer
35
+ surface (`^0.4.0`)**: framing, the claims gate, the refusal guards, and
36
+ assembly; this package keeps custody only (atomic snapshot, sign
37
+ callback, wrong-key guard, post-sign verification, closed errors).
38
+
39
+ ## [0.1.x] — 2026-09-14
40
+
41
+ - Initial public releases: one signing function per artifact kind, the
42
+ key-handle contract with the atomic `keyIdentity` snapshot, the
43
+ wrong-key guard, and post-sign verification through
44
+ `@charter-agreement-protocol/verifier`.
package/NOTICE CHANGED
@@ -1,9 +1,9 @@
1
- Charter Agreement Signer
1
+ Charter Agreement Signer (TypeScript)
2
2
  Copyright 2026 BaseLabs
3
3
 
4
4
  This product includes software developed by BaseLabs
5
5
  (https://github.com/baselabs).
6
6
 
7
- This product depends on the charter_agreement_protocol package
8
- (https://github.com/baselabs/charter_agreement_protocol), also licensed under
9
- the Apache License, Version 2.0.
7
+ This product depends on the @charter-agreement-protocol/verifier package
8
+ (https://github.com/baselabs/charter_agreement_protocol_typescript), also
9
+ licensed under the Apache License, Version 2.0.
package/README.md CHANGED
@@ -8,12 +8,14 @@ Holder-side companion signer for the
8
8
  **CAP verifies; it never authorizes. This signer signs; it never takes
9
9
  custody.** You keep the key (HSM, OS keychain, key server) behind a handle
10
10
  object with two callbacks; the signer resolves ONE atomic `keyIdentity`
11
- snapshot, builds the exact RFC 7515 signing input, runs the honest-signer
12
- refusal guards before any key is touched, checks the returned signature
13
- against the snapshot's public key (the wrong-key guard), assembles the
14
- compact, and post-sign-verifies the assembled artifact through the
15
- `@charter-agreement-protocol/verifier` package — exactly one verification
16
- implementation.
11
+ snapshot, then delegates EVERYTHING pure to the verifier package's producer
12
+ surface — the exact RFC 7515 signing input, the producer claims gate, the
13
+ R1–R3 honest-signer refusal guards (claims-truth, no-equivocation,
14
+ ancestry/governing coverage), and assembly — **before the key signs**, with
15
+ exactly one implementation. What stays here is custody: the sign callback,
16
+ the wrong-key guard against the snapshot's public key, and the post-sign
17
+ verification of the assembled artifact through the
18
+ `@charter-agreement-protocol/verifier` package.
17
19
 
18
20
  ## Install
19
21
 
@@ -27,11 +29,15 @@ Requires Node >= 24.8 (ML-DSA support in the Node builtins).
27
29
 
28
30
  ```js
29
31
  import { signDescriptor } from "@charter-agreement-protocol/signer";
32
+ import { generateKeyPairSync } from "node:crypto";
33
+
34
+ const { publicKey, privateKey } = generateKeyPairSync("ed25519");
35
+ const publicKeyBase64url = publicKey.export({ type: "spki", format: "der" }).subarray(-32).toString("base64url");
30
36
 
31
37
  const handle = {
32
38
  // ONE atomic snapshot — the kid and public key cannot disagree.
33
39
  keyIdentity() {
34
- return { kid: "my-key-001", publicKey }; // base64url raw public key
40
+ return { kid: "my-key-001", publicKey: publicKeyBase64url }; // base64url raw public key
35
41
  },
36
42
  // The holder's job: sign the exact message bytes with the held key.
37
43
  sign(message) {
@@ -44,7 +50,7 @@ const result = await signDescriptor(
44
50
  protocol_revision: 2,
45
51
  descriptor_number: 1,
46
52
  verification_keys: [
47
- { key_id: "my-key-001", algorithm: "Ed25519", public_key: publicKey, status: "active" },
53
+ { key_id: "my-key-001", algorithm: "Ed25519", public_key: publicKeyBase64url, status: "active" },
48
54
  ],
49
55
  attestation_hints: [],
50
56
  extensions: { critical: {}, optional: {} },
@@ -65,27 +71,79 @@ empty context — your handle signs the exact message bytes as with Ed25519).
65
71
  | Export | What it does |
66
72
  |---|---|
67
73
  | `signDescriptor(claims, handle, opts?)` | Signs one Party Descriptor; post-verified via `verifyDescriptor` |
68
- | `signReceipt(claims, handle, chain \| null, opts?)` | Signs one Receipt; post-verified via `verifyReceipt` when a chain view is supplied |
69
- | `signAcceptance(claims, handle, view, opts?)` | Signs one Acceptance against the caller's verified view |
70
- | `signTermination(claims, handle, view, opts?)` | Signs one Termination against the caller's verified view |
74
+ | `signReceipt(claims, handle, chain, opts?)` | Signs one Receipt against the required issuing view; post-verified via `verifyReceipt` (the reference takes a context, never none) |
75
+ | `signAcceptance(claims, handle, view, opts?)` | Signs one Acceptance against the caller's verified view — the R1–R3 refusal guards run over `view.chain` before the key signs |
76
+ | `signTermination(claims, handle, view, opts?)` | Signs one Termination against the caller's verified view — the R1–R3 refusal guards run over `view.chain` before the key signs |
77
+
78
+ The acceptance/termination `view` is `{ revisionText, descriptorCompacts,
79
+ chain }`: the revision the claims name, the signing party's descriptor
80
+ chain, and the caller's full `ChainView` (`{ revisions, acceptances,
81
+ descriptors, terminations }`) the refusal guards verify and bind against.
71
82
 
72
83
  Errors are closed: `{ ok: false, error: "invalid_handle" \| "signing_failed"
73
- \| "invalid_input" \| "refused", code? }`. A signing failure is never a
74
- silent pass.
84
+ \| "invalid_input" \| "refused" \| "verification_failed" }` — `code` rides
85
+ only on `invalid_input` and `refused`; `verification_failed` is bare
86
+ (reference parity).
87
+ `"refused"` is the honest-signer refusal: the claims contradict the caller's
88
+ own verified view, and the handle's `sign` callback is never reached (the
89
+ atomic `keyIdentity` snapshot resolves first — the reference ordering).
90
+ `"verification_failed"` is the separate post-sign discipline: the assembled
91
+ artifact did not verify against the caller's view (for example a key that is
92
+ not in the party descriptor's `verification_keys`). Malformed claims are
93
+ rejected pre-sign as `"invalid_input"` through the verifier package's
94
+ producer claims gate — they never burn a key operation. A signing failure is
95
+ never a silent pass.
96
+
97
+ ## The sibling verifier
98
+
99
+ This package is one of two independent TypeScript siblings implementing the
100
+ Charter Agreement Protocol — no Elixir code or dependency at runtime; the
101
+ Elixir reference is the certification oracle (the verifier's vendored
102
+ corpus is certified against it, and its CI cross-verifies
103
+ TypeScript-signed artifacts from raw bytes).
104
+
105
+ - **This package** — `@charter-agreement-protocol/signer`: key custody,
106
+ the wrong-key guard, post-sign verification, and the closed error
107
+ vocabulary — all protocol logic delegated to the verifier.
108
+ - [`@charter-agreement-protocol/verifier`](https://www.npmjs.com/package/@charter-agreement-protocol/verifier)
109
+ — verification and the pure producer surface (signing inputs, claims
110
+ gate, refusal guards, assembly) that this package signs through.
111
+
112
+ The pair releases together: this package depends on the verifier (`^0.4.0`),
113
+ so a verifier release stages first and the signer locks and releases
114
+ against the published version immediately after.
75
115
 
76
116
  ## Evidence
77
117
 
78
118
  - The Elixir reference repository's gate verifies TypeScript-signed
79
119
  artifacts from raw bytes (cross-implementation agreement, enforced in CI).
120
+ - Framing, the producer claims gate, refusal guards, and assembly are the
121
+ verifier package's producer surface (0.4.0) — the same single
122
+ implementation the independent verifier certifies. What remains here is
123
+ custody plus input policing: the issuing-view discipline composes the
124
+ verifier's own `verifyChain`, never a local reimplementation.
80
125
  - Post-sign verification goes through the
81
126
  [@charter-agreement-protocol/verifier](https://www.npmjs.com/package/@charter-agreement-protocol/verifier)
82
127
  package — the certified dual-implementation-verified verifier.
83
128
 
129
+ ## The verify side
130
+
131
+ This package tracks the published verify line: it depends on
132
+ `@charter-agreement-protocol/verifier` `^0.5.0`. Verifier 0.5.0 added
133
+ consumer-side surface only; the producer surface this package uses is
134
+ unchanged. The producer-side capability alignment (mirrored
135
+ `capabilities()` with refuse-before-mint, declared minting metadata,
136
+ profile-bound signing) is the next feature release on this line.
137
+
84
138
  ## SemVer
85
139
 
86
- Package SemVer decoupled from the protocol's `protocol_revision`; a package
87
- major is owed only when a shipped public API is removed or changes
88
- behavior.
140
+ Package SemVer decoupled from the protocol's `protocol_revision`. While the
141
+ package is below 1.0, breaking public-API changes land in minor versions and
142
+ are named in the commit message (0.2.0 is one: `signTermination`'s view
143
+ requires `chain`, `signAcceptance` now enforces the refusal guards over the
144
+ `chain` it previously ignored, and `signReceipt` requires its issuing view);
145
+ at or above 1.0 a package major is owed when a shipped public API is removed
146
+ or changes behavior.
89
147
 
90
148
  ## License
91
149
 
package/dist/index.js CHANGED
@@ -2,7 +2,8 @@
2
2
  //
3
3
  // Public surface of the holder-side companion signer: one signing function
4
4
  // per artifact kind, the key-handle contract, and the closed error
5
- // vocabulary. Verification of assembled artifacts goes through
6
- // @charter-agreement-protocol/verifier — exactly one verification
7
- // implementation.
5
+ // vocabulary. ALL protocol logic — framing, the producer claims gate, the
6
+ // R1-R3 refusal guards, assembly, and verification of the assembled
7
+ // artifact — goes through @charter-agreement-protocol/verifier: exactly
8
+ // one implementation. This package holds the custody half only.
8
9
  export { signDescriptor, signReceipt, signAcceptance, signTermination, } from "./signer.js";
package/dist/signer.d.ts CHANGED
@@ -16,14 +16,16 @@ export type SignError = {
16
16
  } | {
17
17
  error: "refused";
18
18
  code: string;
19
+ } | {
20
+ error: "verification_failed";
19
21
  };
20
22
  export type SignOk<T> = {
21
23
  ok: true;
22
24
  result: T;
23
25
  };
24
- export type SignResult<T> = SignOk<T> | {
26
+ export type SignResult<T> = SignOk<T> | ({
25
27
  ok: false;
26
- } & SignError;
28
+ } & SignError);
27
29
  export type ChainView = {
28
30
  revisions: string[];
29
31
  acceptances: string[];
@@ -35,7 +37,7 @@ export declare function signDescriptor(claims: Record<string, unknown>, keyHandl
35
37
  }): Promise<SignResult<{
36
38
  descriptor: string;
37
39
  }>>;
38
- export declare function signReceipt(claims: Record<string, unknown>, keyHandle: unknown, chain: ChainView | null, opts?: {
40
+ export declare function signReceipt(claims: Record<string, unknown>, keyHandle: unknown, chain: ChainView, opts?: {
39
41
  algorithm?: string;
40
42
  }): Promise<SignResult<{
41
43
  receipt: string;
@@ -52,6 +54,7 @@ export declare function signAcceptance(claims: Record<string, unknown>, keyHandl
52
54
  export declare function signTermination(claims: Record<string, unknown>, keyHandle: unknown, view: {
53
55
  revisionText: string;
54
56
  descriptorCompacts: string[];
57
+ chain: ChainView;
55
58
  }, opts?: {
56
59
  algorithm?: string;
57
60
  }): Promise<SignResult<{
package/dist/signer.js CHANGED
@@ -7,21 +7,15 @@
7
7
  // keyIdentity(handle) -> { kid, publicKey } // ONE atomic snapshot
8
8
  // sign(message, handle) -> signature bytes // the holder's job
9
9
  //
10
- // The signer builds the exact RFC 7515 signing input through the verifier
11
- // package's producers surface, runs the honest-signer refusal guards
12
- // BEFORE any key is touched, checks the returned signature against the
13
- // snapshot's public key (the wrong-key guard), assembles the compact, and
14
- // post-sign-verifies the assembled artifact through the verifier package.
15
- // It never verifies third-party artifacts, never transports, never
16
- // persists, and never sees a private key.
17
- import { algorithmRegistry, canonical, decodeArtifact, defaultEmissionName, emissions, encodeBase64url, verifyAcceptance, verifyDescriptor, verifyReceipt, verifySignature, verifyTermination, } from "@charter-agreement-protocol/verifier";
18
- const TYPES = {
19
- descriptor: "cap+party",
20
- acceptance: "cap+acceptance",
21
- termination: "cap+termination",
22
- receipt: "cap+receipt",
23
- };
24
- const MAX_INPUT_BYTES = 1_048_576;
10
+ // Framing, the producer claims gate, the honest-signer refusal guards
11
+ // (R1–R3), and assembly are ALL the verifier package's producer surface —
12
+ // exactly one implementation, exactly the reference signer's delegation.
13
+ // This package adds the custody half: the atomic key snapshot, the sign
14
+ // callback, the wrong-key guard against the snapshot's public key, and the
15
+ // post-sign verification of the assembled artifact. It never verifies
16
+ // third-party artifacts, never transports, never persists, and never sees
17
+ // a private key.
18
+ import { acceptanceSigningInput, algorithmRegistry, assembleCompact, descriptorSigningInput, receiptSigningInput, terminationSigningInput, verifyAcceptance, verifyChain, verifyDescriptor, verifyReceipt, verifySignature, verifyTermination, } from "@charter-agreement-protocol/verifier";
25
19
  function registryRow(algorithm) {
26
20
  return algorithmRegistry().find((row) => row.name === algorithm) ?? null;
27
21
  }
@@ -44,66 +38,27 @@ async function resolveKeyIdentity(handle) {
44
38
  return { error: "invalid_handle" };
45
39
  }
46
40
  }
47
- function resolveAlgorithm(selection) {
48
- if (selection === undefined || selection === null)
49
- return defaultEmissionName();
50
- if (typeof selection === "string" && selection in emissions())
51
- return selection;
52
- return { error: "invalid_input", code: "algorithm_unsupported" };
53
- }
54
- function claimsRevision(claims) {
55
- const value = claims.protocol_revision;
56
- return typeof value === "number" && Number.isInteger(value) ? value : null;
57
- }
58
- // The producer core: canonical claims, the closed protected header with the
59
- // selected emission name and the SNAPSHOT kid, the size guard, and the
60
- // provisional framing check over a zero signature — CAP's producer contract.
61
- function frameSigningInput(kind, claims, kid, algorithm) {
62
- if (!claims || typeof claims !== "object" || Array.isArray(claims)) {
63
- return { error: "invalid_input", code: "invalid_type" };
64
- }
65
- if (claimsRevision(claims) !== emissions()[algorithm]) {
66
- return { error: "invalid_input", code: "signing_input_invalid" };
67
- }
68
- if (typeof kid !== "string" || kid.length === 0 || kid.length > 128 || !/^[A-Za-z0-9._~-]+$/.test(kid)) {
69
- return { error: "invalid_input", code: "signing_input_invalid" };
70
- }
71
- const row = registryRow(algorithm);
72
- if (!row)
73
- return { error: "invalid_input", code: "algorithm_unsupported" };
74
- const payloadBytes = Buffer.from(canonical(claims), "utf8");
75
- const protectedBytes = Buffer.from(canonical({ alg: algorithm, kid, typ: TYPES[kind] }), "utf8");
76
- const protectedSegment = encodeBase64url(protectedBytes);
77
- const payloadSegment = encodeBase64url(payloadBytes);
78
- const message = Buffer.from(`${protectedSegment}.${payloadSegment}`, "utf8");
79
- const signatureSegmentLength = Math.ceil((row.signatureBytes * 4) / 3);
80
- if (message.length + 1 + signatureSegmentLength > MAX_INPUT_BYTES) {
81
- return { error: "invalid_input", code: "signing_input_invalid" };
82
- }
83
- // The provisional check: a zero signature of the row's exact length must
84
- // frame and bind (canonical bytes, registry binding, per-row length).
85
- const zeroSignature = Buffer.alloc(row.signatureBytes);
86
- const provisional = `${protectedSegment}.${payloadSegment}.${encodeBase64url(zeroSignature)}`;
87
- const decoded = decodeArtifact(provisional);
88
- if (!decoded.ok)
89
- return { error: "invalid_input", code: "signing_input_invalid" };
90
- return { protectedSegment, payloadSegment, message };
91
- }
92
- async function signCommon(kind, claims, keyHandle, opts) {
41
+ // The shared custody tail (the reference signer's): identity snapshot ->
42
+ // producer (framing, claims schema, R1-R3 refusals - one implementation,
43
+ // in the verifier package) -> sign via the handle -> wrong-key guard.
44
+ // A refusal or producer rejection never touches the key.
45
+ async function signProduced(keyHandle, produce, opts) {
93
46
  const snapshot = await resolveKeyIdentity(keyHandle);
94
47
  if ("error" in snapshot)
95
48
  return snapshot;
96
- const algorithm = resolveAlgorithm(opts?.algorithm);
97
- if (typeof algorithm !== "string" && "error" in algorithm)
98
- return algorithm;
99
- const framed = frameSigningInput(kind, claims, snapshot.kid, algorithm);
100
- if ("error" in framed)
101
- return framed;
49
+ const produced = produce(snapshot.kid, opts?.algorithm);
50
+ if (!produced.ok) {
51
+ // signing_refused is the honest-signer refusal; every other producer
52
+ // code stays caller input.
53
+ return produced.code === "signing_refused"
54
+ ? { error: "refused", code: produced.code }
55
+ : { error: "invalid_input", code: produced.code };
56
+ }
102
57
  // The holder signs the exact message; a fault or a wrong-length result is
103
58
  // a signing failure, never a silent pass.
104
59
  let signature;
105
60
  try {
106
- signature = await keyHandle.sign(framed.message, keyHandle);
61
+ signature = await keyHandle.sign(produced.input.message, keyHandle);
107
62
  }
108
63
  catch (_fault) {
109
64
  return { error: "signing_failed" };
@@ -111,66 +66,115 @@ async function signCommon(kind, claims, keyHandle, opts) {
111
66
  if (!(signature instanceof Uint8Array) || signature.length === 0) {
112
67
  return { error: "signing_failed" };
113
68
  }
114
- const row = registryRow(algorithm);
69
+ const row = registryRow(produced.input.alg);
115
70
  if (!row || signature.length !== row.signatureBytes) {
116
71
  return { error: "signing_failed" };
117
72
  }
118
73
  // The wrong-key guard: the signature MUST verify against the snapshot's
119
- // public key under the selected algorithm.
120
- if (!verifySignature(framed.message, Buffer.from(signature), snapshot.publicKey, algorithm)) {
74
+ // public key under the minted algorithm.
75
+ if (!verifySignature(produced.input.message, Buffer.from(signature), snapshot.publicKey, produced.input.alg)) {
121
76
  return { error: "signing_failed" };
122
77
  }
123
- return { message: framed.message, signature: Buffer.from(signature) };
78
+ return { input: produced.input, signature: Buffer.from(signature) };
124
79
  }
125
- function assemble(message, signature) {
126
- return `${message.toString("utf8")}.${encodeBase64url(signature)}`;
80
+ // Assembly goes through the verifier's assembleCompact: registry-row
81
+ // length, framing re-decode, size gate - never a local string join.
82
+ function compactOrInvalid(signed) {
83
+ const assembled = assembleCompact(signed.input, signed.signature);
84
+ if (!assembled.ok)
85
+ return { error: "invalid_input", code: assembled.code };
86
+ return { compact: assembled.compact };
87
+ }
88
+ // The view's chain is THIS package's public contract (the producer surface
89
+ // validates its depth); the shallow shape is policed here so the closed
90
+ // error vocabulary holds regardless of which verifier revision resolves.
91
+ // A verifier crash past this gate stays loud - never a misleading error.
92
+ function chainViewOrInvalid(chain) {
93
+ if (!chain || typeof chain !== "object" || Array.isArray(chain)) {
94
+ return { error: "invalid_input", code: "signing_input_invalid" };
95
+ }
96
+ return chain;
97
+ }
98
+ // A missing view object is caller input like a missing chain: the closed
99
+ // error, never a dereference throw.
100
+ function viewChainOrInvalid(view) {
101
+ if (!view || typeof view !== "object")
102
+ return { error: "invalid_input", code: "signing_input_invalid" };
103
+ return chainViewOrInvalid(view.chain);
127
104
  }
128
- // ---------------------------------------------------------------------------
129
- // Honest-signer refusal guards (R1–R3 from the Elixir reference): they run
130
- // against the caller's own verified view BEFORE any key is touched.
131
- // ---------------------------------------------------------------------------
132
105
  // ---------------------------------------------------------------------------
133
106
  // Public signing surface
134
107
  // ---------------------------------------------------------------------------
135
108
  export async function signDescriptor(claims, keyHandle, opts = {}) {
136
- const signed = await signCommon("descriptor", claims, keyHandle, opts);
109
+ const signed = await signProduced(keyHandle, (kid, algorithm) => descriptorSigningInput(kid, claims, algorithm), opts);
137
110
  if ("error" in signed)
138
111
  return { ok: false, ...signed };
139
- const compact = assemble(signed.message, signed.signature);
140
- const verified = verifyDescriptor(compact);
112
+ const assembled = compactOrInvalid(signed);
113
+ if ("error" in assembled)
114
+ return { ok: false, ...assembled };
115
+ const verified = verifyDescriptor(assembled.compact);
141
116
  if (!verified.ok)
142
- return { ok: false, error: "invalid_input", code: verified.code };
143
- return { ok: true, result: { descriptor: compact } };
117
+ return { ok: false, error: "verification_failed" };
118
+ return { ok: true, result: { descriptor: assembled.compact } };
144
119
  }
145
120
  export async function signReceipt(claims, keyHandle, chain, opts = {}) {
146
- const signed = await signCommon("receipt", claims, keyHandle, opts);
121
+ // The issuing view is REQUIRED (the reference takes ChainFacts or a
122
+ // CharterRevision, never none) and its discipline runs after the build,
123
+ // before the key - the reference's context is a verified ChainFacts by
124
+ // construction, so a TS view that fails the chain discipline is caller
125
+ // input, never a burned key operation.
126
+ const signed = await signProduced(keyHandle, (kid, algorithm) => {
127
+ const built = receiptSigningInput(kid, claims, algorithm);
128
+ if (!built.ok)
129
+ return built;
130
+ const resolved = chainViewOrInvalid(chain);
131
+ if ("error" in resolved)
132
+ return { ok: false, code: resolved.code };
133
+ if (!verifyChain(resolved).ok)
134
+ return { ok: false, code: "chain_invalid" };
135
+ return built;
136
+ }, opts);
147
137
  if ("error" in signed)
148
138
  return { ok: false, ...signed };
149
- const compact = assemble(signed.message, signed.signature);
150
- if (chain) {
151
- const verified = verifyReceipt(compact, chain);
152
- if (!verified.ok)
153
- return { ok: false, error: "invalid_input", code: verified.code };
154
- }
155
- return { ok: true, result: { receipt: compact } };
139
+ const assembled = compactOrInvalid(signed);
140
+ if ("error" in assembled)
141
+ return { ok: false, ...assembled };
142
+ const verified = verifyReceipt(assembled.compact, chain);
143
+ if (!verified.ok)
144
+ return { ok: false, error: "verification_failed" };
145
+ return { ok: true, result: { receipt: assembled.compact } };
156
146
  }
157
147
  export async function signAcceptance(claims, keyHandle, view, opts = {}) {
158
- const signed = await signCommon("acceptance", claims, keyHandle, opts);
148
+ const viewChain = viewChainOrInvalid(view);
149
+ const signed = await signProduced(keyHandle, (kid, algorithm) => {
150
+ if ("error" in viewChain)
151
+ return { ok: false, code: viewChain.code };
152
+ return acceptanceSigningInput(kid, claims, viewChain, algorithm);
153
+ }, opts);
159
154
  if ("error" in signed)
160
155
  return { ok: false, ...signed };
161
- const compact = assemble(signed.message, signed.signature);
162
- const verified = verifyAcceptance(compact, view.revisionText, view.descriptorCompacts);
156
+ const assembled = compactOrInvalid(signed);
157
+ if ("error" in assembled)
158
+ return { ok: false, ...assembled };
159
+ const verified = verifyAcceptance(assembled.compact, view.revisionText, view.descriptorCompacts);
163
160
  if (!verified.ok)
164
- return { ok: false, error: "invalid_input", code: verified.code };
165
- return { ok: true, result: { acceptance: compact } };
161
+ return { ok: false, error: "verification_failed" };
162
+ return { ok: true, result: { acceptance: assembled.compact } };
166
163
  }
167
164
  export async function signTermination(claims, keyHandle, view, opts = {}) {
168
- const signed = await signCommon("termination", claims, keyHandle, opts);
165
+ const viewChain = viewChainOrInvalid(view);
166
+ const signed = await signProduced(keyHandle, (kid, algorithm) => {
167
+ if ("error" in viewChain)
168
+ return { ok: false, code: viewChain.code };
169
+ return terminationSigningInput(kid, claims, viewChain, algorithm);
170
+ }, opts);
169
171
  if ("error" in signed)
170
172
  return { ok: false, ...signed };
171
- const compact = assemble(signed.message, signed.signature);
172
- const verified = verifyTermination(compact, view.revisionText, view.descriptorCompacts);
173
+ const assembled = compactOrInvalid(signed);
174
+ if ("error" in assembled)
175
+ return { ok: false, ...assembled };
176
+ const verified = verifyTermination(assembled.compact, view.revisionText, view.descriptorCompacts);
173
177
  if (!verified.ok)
174
- return { ok: false, error: "invalid_input", code: verified.code };
175
- return { ok: true, result: { termination: compact } };
178
+ return { ok: false, error: "verification_failed" };
179
+ return { ok: true, result: { termination: assembled.compact } };
176
180
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@charter-agreement-protocol/signer",
3
- "version": "0.1.1",
4
- "description": "Holder-side companion signer for the Charter Agreement Protocol (CAP) in TypeScript — atomic key snapshots, honest-signer refusals, external-signature assembly, and post-sign verification through the @charter-agreement-protocol/verifier. CAP verifies; it never authorizes; this signer signs, it never verifies third-party artifacts or takes custody.",
3
+ "version": "0.2.1",
4
+ "description": "Holder-side companion signer for the Charter Agreement Protocol (CAP) in TypeScript - key custody only: an atomic key snapshot, the sign callback, the wrong-key guard, and post-sign verification, with framing, the claims gate, the R1-R3 honest-signer refusals, and assembly all delegated to the @charter-agreement-protocol/verifier producer surface. CAP verifies; it never authorizes; this signer signs, it never verifies third-party artifacts or takes custody.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
7
7
  "main": "./dist/index.js",
@@ -14,6 +14,7 @@
14
14
  "./package.json": "./package.json"
15
15
  },
16
16
  "files": [
17
+ "CHANGELOG.md",
17
18
  "dist",
18
19
  "LICENSE",
19
20
  "NOTICE",
@@ -26,13 +27,19 @@
26
27
  "build": "tsc -p tsconfig.build.json",
27
28
  "typecheck": "tsc --noEmit",
28
29
  "test": "node --test --experimental-strip-types --no-warnings signer.test.ts",
29
- "prepack": "npm run build"
30
+ "prepack": "npm run build",
31
+ "typecheck:web": "tsc -p tsconfig.web.json",
32
+ "build:site": "node web/build.mjs && esbuild web/main.ts --bundle --minify --format=iife --target=es2022 --outfile=site-dist/app.js --alias:node:crypto=./web/shim/node-crypto.ts --alias:node:fs=./web/shim/node-fs.ts --alias:node:path=./web/shim/node-path.ts --inject:./web/shim/buffer-global.ts"
30
33
  },
31
34
  "dependencies": {
32
- "@charter-agreement-protocol/verifier": "^0.2.0"
35
+ "@charter-agreement-protocol/verifier": "^0.5.0"
33
36
  },
34
37
  "devDependencies": {
38
+ "@noble/ed25519": "^3.2.0",
39
+ "@noble/hashes": "^2.4.0",
35
40
  "@types/node": "^26.5.1",
41
+ "buffer": "^6.0.3",
42
+ "esbuild": "^0.28.2",
36
43
  "typescript": "~5.9.0"
37
44
  },
38
45
  "repository": {