@visa/cli 4.1.0-rc.203 → 4.1.0-rc.205

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.
@@ -198,6 +198,8 @@ export interface CardDrawVerdictDraw {
198
198
  export interface CardDrawVerdictCapability {
199
199
  /** Opaque to the engine — passed straight back to {@link CardDrawVerdictSeam.fetchVerdict}. */
200
200
  agentKey: unknown;
201
+ /** Root-signed delegation for a short-lived device key. */
202
+ runtimeCertificate?: string;
201
203
  agentJkt: string;
202
204
  /** Auth origin that minted the binding and hosts the /v4/card/draw* routes. */
203
205
  authBaseUrl: string;
@@ -207,6 +209,7 @@ export interface CardDrawVerdictSeam {
207
209
  fetchVerdict: (input: {
208
210
  authBaseUrl: string;
209
211
  agentKey: unknown;
212
+ runtimeCertificate?: string;
210
213
  mandateId: string;
211
214
  drawId: string;
212
215
  draw: CardDrawVerdictDraw;
@@ -218,12 +221,14 @@ export interface CardDrawVerdictSeam {
218
221
  export interface CardMandateRegisterSeam {
219
222
  loadCapability: (agentRef?: string) => {
220
223
  agentKey: unknown;
224
+ runtimeCertificate?: string;
221
225
  agentJkt: string;
222
226
  authBaseUrl: string;
223
227
  } | null;
224
228
  register: (input: {
225
229
  authBaseUrl: string;
226
230
  agentKey: unknown;
231
+ runtimeCertificate?: string;
227
232
  mandateId: string;
228
233
  mintToken: string;
229
234
  ceiling: string;
@@ -241,6 +241,9 @@ export function createCliCheckoutEngine(deps = {}) {
241
241
  .register({
242
242
  authBaseUrl: args.registerCap.authBaseUrl,
243
243
  agentKey: args.registerCap.agentKey,
244
+ ...(args.registerCap.runtimeCertificate
245
+ ? { runtimeCertificate: args.registerCap.runtimeCertificate }
246
+ : {}),
244
247
  mandateId: args.mandateId,
245
248
  mintToken: args.mintToken,
246
249
  ceiling: args.ceiling,
@@ -804,6 +807,9 @@ export function createCliCheckoutEngine(deps = {}) {
804
807
  const { verdict } = await verdictSeam.fetchVerdict({
805
808
  authBaseUrl: capability.authBaseUrl,
806
809
  agentKey: capability.agentKey,
810
+ ...(capability.runtimeCertificate
811
+ ? { runtimeCertificate: capability.runtimeCertificate }
812
+ : {}),
807
813
  mandateId,
808
814
  // One draw per review; the reviewId is auth's idempotency key.
809
815
  drawId: input.reviewId,
@@ -51,7 +51,7 @@ export type CreateCardMandateDeps = {
51
51
  ledger: MandateLedger;
52
52
  /** verify-web origin the intent is minted against (persisted for later draws). */
53
53
  approvalBaseUrl: string;
54
- /** Scoped mint token to persist for tap-free draws. */
54
+ /** @deprecated Accepted from older callers but deliberately never persisted. */
55
55
  mintToken?: string;
56
56
  now?: () => Date;
57
57
  };
@@ -123,7 +123,6 @@ export async function createCardMandate(input, deps) {
123
123
  merchantHost: host,
124
124
  merchantCountryCode: input.merchant.countryCode.toUpperCase(),
125
125
  approvalBaseUrl: deps.approvalBaseUrl,
126
- ...(deps.mintToken ? { mintToken: deps.mintToken } : {}),
127
126
  expiresAt: new Date(expiryMs).toISOString(),
128
127
  maxDraws,
129
128
  createdAt: now.toISOString(),
@@ -31,11 +31,7 @@ export type CardMandateRecord = {
31
31
  merchantCountryCode: string;
32
32
  /** verify-web origin the ceiling intent was minted against. */
33
33
  approvalBaseUrl: string;
34
- /**
35
- * Scoped token released by the ceiling approval. It bootstraps the one intent
36
- * + register ceremony only. Each cryptogram AND its outcome confirmation use
37
- * the exact per-draw verdict instead. Owner-only (0600).
38
- */
34
+ /** @deprecated Read-migration only. New writers never persist this bearer. */
39
35
  mintToken?: string;
40
36
  expiresAt: string;
41
37
  /** Network purchase-count cap disclosed at approval. Absent on legacy rows. */
@@ -11,8 +11,8 @@
11
11
  //
12
12
  // VGS's decline_threshold is a per-transaction NETWORK control; this ledger is
13
13
  // the SEPARATE cumulative budget control. The file holds identifiers and
14
- // accounting state plus the scoped mint token (owner-only) — never PAN, DPAN,
15
- // CVC, or cryptogram values. Cross-process concurrency is NOT solved by a file
14
+ // accounting state only never a bootstrap mint token, PAN, DPAN, CVC, or
15
+ // cryptogram values. Cross-process concurrency is NOT solved by a file
16
16
  // (same caveat csmoove notes); the in-process mutex below serializes draws
17
17
  // within one CLI process, which is the live-spike target.
18
18
  //
@@ -121,6 +121,18 @@ export class MandateLedger {
121
121
  if (!doc || !Array.isArray(doc.mandates)) {
122
122
  return { version: CARD_MANDATE_LEDGER_VERSION, mandates: [] };
123
123
  }
124
+ // One-time migration for pre-hardening ledgers. The budget mint is a
125
+ // bootstrap bearer used only during create/register and must not survive
126
+ // process exit. Scrub it eagerly on read, not merely on the next mutation.
127
+ let scrubbedBootstrapToken = false;
128
+ for (const mandate of doc.mandates) {
129
+ if (Object.prototype.hasOwnProperty.call(mandate, 'mintToken')) {
130
+ delete mandate.mintToken;
131
+ scrubbedBootstrapToken = true;
132
+ }
133
+ }
134
+ if (scrubbedBootstrapToken)
135
+ await writeOwnerOnlyJson(this.path, doc);
124
136
  // Self-heal: prune reservations stranded by a crash before commit/release
125
137
  // so `remaining` reflects reality. In-memory here; a mutating caller then
126
138
  // persists the pruned state via save().
@@ -161,9 +173,12 @@ export class MandateLedger {
161
173
  const exp = Date.parse(m.expiresAt);
162
174
  return !Number.isFinite(exp) || exp >= pruneBefore;
163
175
  });
164
- file.mandates.push(record);
176
+ // Even an older caller that still supplies the deprecated field cannot
177
+ // persist the bootstrap bearer.
178
+ const { mintToken: _discardedBootstrapToken, ...safeRecord } = record;
179
+ file.mandates.push(safeRecord);
165
180
  await this.save(file);
166
- return record;
181
+ return safeRecord;
167
182
  });
168
183
  }
169
184
  /** Read a single mandate (no lock — a snapshot copy). */