@yielded/auth-persistence 0.1.0-beta.10

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.
Files changed (182) hide show
  1. package/README.md +27 -0
  2. package/dist/Adapter.d.mts +30 -0
  3. package/dist/Adapter.mjs +25 -0
  4. package/dist/index.d.mts +7 -0
  5. package/dist/index.mjs +7 -0
  6. package/dist/internal/PasswordPreparedPostconditions.d.mts +22 -0
  7. package/dist/internal/PasswordPreparedPostconditions.mjs +12 -0
  8. package/dist/internal/PasswordPreparedPostconditions.mjs.map +1 -0
  9. package/dist/internal/configuration.d.mts +174 -0
  10. package/dist/internal/configuration.mjs +7 -0
  11. package/dist/internal/configuration.mjs.map +1 -0
  12. package/dist/internal/email-kernel.d.mts +275 -0
  13. package/dist/internal/email-kernel.mjs +487 -0
  14. package/dist/internal/email-kernel.mjs.map +1 -0
  15. package/dist/internal/mapping-error.d.mts +10 -0
  16. package/dist/internal/mapping-error.mjs +30 -0
  17. package/dist/internal/mapping-error.mjs.map +1 -0
  18. package/dist/internal/models/common.d.mts +12 -0
  19. package/dist/internal/models/email-model.d.mts +353 -0
  20. package/dist/internal/models/email-model.mjs +21 -0
  21. package/dist/internal/models/email-model.mjs.map +1 -0
  22. package/dist/internal/models/passkey-model.d.mts +266 -0
  23. package/dist/internal/models/passkey-model.mjs +34 -0
  24. package/dist/internal/models/passkey-model.mjs.map +1 -0
  25. package/dist/internal/models/passkey-registration-ceremony-model.d.mts +49 -0
  26. package/dist/internal/models/passkey-registration-ceremony-model.mjs +18 -0
  27. package/dist/internal/models/passkey-registration-ceremony-model.mjs.map +1 -0
  28. package/dist/internal/models/passkey-write-model.d.mts +189 -0
  29. package/dist/internal/models/passkey-write-model.mjs +13 -0
  30. package/dist/internal/models/passkey-write-model.mjs.map +1 -0
  31. package/dist/internal/models/password-model.d.mts +328 -0
  32. package/dist/internal/models/password-model.mjs +23 -0
  33. package/dist/internal/models/password-model.mjs.map +1 -0
  34. package/dist/internal/models/proof-model.d.mts +285 -0
  35. package/dist/internal/models/proof-model.mjs +17 -0
  36. package/dist/internal/models/proof-model.mjs.map +1 -0
  37. package/dist/internal/models/session-model.d.mts +199 -0
  38. package/dist/internal/models/session-model.mjs +20 -0
  39. package/dist/internal/models/session-model.mjs.map +1 -0
  40. package/dist/internal/models/step-up-model.d.mts +66 -0
  41. package/dist/internal/models/step-up-model.mjs +9 -0
  42. package/dist/internal/models/step-up-model.mjs.map +1 -0
  43. package/dist/internal/oauth-app.d.mts +11 -0
  44. package/dist/internal/oauth-app.mjs +57 -0
  45. package/dist/internal/oauth-app.mjs.map +1 -0
  46. package/dist/internal/oauth-server.d.mts +10 -0
  47. package/dist/internal/oauth-server.mjs +66 -0
  48. package/dist/internal/oauth-server.mjs.map +1 -0
  49. package/dist/internal/passkey/admission.d.mts +6 -0
  50. package/dist/internal/passkey/admission.mjs +216 -0
  51. package/dist/internal/passkey/admission.mjs.map +1 -0
  52. package/dist/internal/passkey/credentials.d.mts +14 -0
  53. package/dist/internal/passkey/credentials.mjs +249 -0
  54. package/dist/internal/passkey/credentials.mjs.map +1 -0
  55. package/dist/internal/passkey/enrollment.mjs +138 -0
  56. package/dist/internal/passkey/enrollment.mjs.map +1 -0
  57. package/dist/internal/passkey/flow.d.mts +21 -0
  58. package/dist/internal/passkey/flow.mjs +452 -0
  59. package/dist/internal/passkey/flow.mjs.map +1 -0
  60. package/dist/internal/passkey/management.mjs +231 -0
  61. package/dist/internal/passkey/management.mjs.map +1 -0
  62. package/dist/internal/passkey/registration-ceremony.d.mts +9 -0
  63. package/dist/internal/passkey/registration-ceremony.mjs +129 -0
  64. package/dist/internal/passkey/registration-ceremony.mjs.map +1 -0
  65. package/dist/internal/passkey/registration-custody.d.mts +2 -0
  66. package/dist/internal/passkey/registration-custody.mjs +52 -0
  67. package/dist/internal/passkey/registration-custody.mjs.map +1 -0
  68. package/dist/internal/passkey/registration-write.mjs +211 -0
  69. package/dist/internal/passkey/registration-write.mjs.map +1 -0
  70. package/dist/internal/passkey/state.d.mts +10 -0
  71. package/dist/internal/passkey/state.mjs +234 -0
  72. package/dist/internal/passkey/state.mjs.map +1 -0
  73. package/dist/internal/passkey/target.d.mts +19 -0
  74. package/dist/internal/passkey/target.mjs +190 -0
  75. package/dist/internal/passkey/target.mjs.map +1 -0
  76. package/dist/internal/passkey/write-state.d.mts +17 -0
  77. package/dist/internal/passkey/write-state.mjs +345 -0
  78. package/dist/internal/passkey/write-state.mjs.map +1 -0
  79. package/dist/internal/passkey/write-target.mjs +192 -0
  80. package/dist/internal/passkey/write-target.mjs.map +1 -0
  81. package/dist/internal/passkey-kernel.d.mts +5592 -0
  82. package/dist/internal/passkey-kernel.mjs +49 -0
  83. package/dist/internal/passkey-kernel.mjs.map +1 -0
  84. package/dist/internal/passkeys.d.mts +7 -0
  85. package/dist/internal/passkeys.mjs +368 -0
  86. package/dist/internal/passkeys.mjs.map +1 -0
  87. package/dist/internal/password-kernel.d.mts +290 -0
  88. package/dist/internal/password-kernel.mjs +704 -0
  89. package/dist/internal/password-kernel.mjs.map +1 -0
  90. package/dist/internal/persistence.d.mts +25 -0
  91. package/dist/internal/persistence.mjs +291 -0
  92. package/dist/internal/persistence.mjs.map +1 -0
  93. package/dist/internal/phone-kernel.d.mts +162 -0
  94. package/dist/internal/phone-kernel.mjs +434 -0
  95. package/dist/internal/phone-kernel.mjs.map +1 -0
  96. package/dist/internal/proof-kernel.d.mts +93 -0
  97. package/dist/internal/proof-kernel.mjs +619 -0
  98. package/dist/internal/proof-kernel.mjs.map +1 -0
  99. package/dist/internal/query-operations.d.mts +61 -0
  100. package/dist/internal/registration-contract.d.mts +16 -0
  101. package/dist/internal/registration.mjs +66 -0
  102. package/dist/internal/registration.mjs.map +1 -0
  103. package/dist/internal/session-kernel.d.mts +48 -0
  104. package/dist/internal/session-kernel.mjs +737 -0
  105. package/dist/internal/session-kernel.mjs.map +1 -0
  106. package/dist/internal/sql-persistence.d.mts +10 -0
  107. package/dist/internal/sql-persistence.mjs +37 -0
  108. package/dist/internal/sql-persistence.mjs.map +1 -0
  109. package/dist/internal/sql-query.mjs +207 -0
  110. package/dist/internal/sql-query.mjs.map +1 -0
  111. package/dist/internal/sql-table.d.mts +36 -0
  112. package/dist/internal/sql-table.mjs +81 -0
  113. package/dist/internal/sql-table.mjs.map +1 -0
  114. package/dist/internal/standalone.d.mts +9 -0
  115. package/dist/internal/standalone.mjs +13 -0
  116. package/dist/internal/standalone.mjs.map +1 -0
  117. package/dist/internal/step-up-state.d.mts +49 -0
  118. package/dist/internal/step-up-state.mjs +32 -0
  119. package/dist/internal/step-up-state.mjs.map +1 -0
  120. package/dist/internal/storage-mapping.mjs +686 -0
  121. package/dist/internal/storage-mapping.mjs.map +1 -0
  122. package/dist/internal/storage-tables.d.mts +913 -0
  123. package/dist/internal/storage-tables.mjs +387 -0
  124. package/dist/internal/storage-tables.mjs.map +1 -0
  125. package/dist/internal/storage-validation.mjs +58 -0
  126. package/dist/internal/storage-validation.mjs.map +1 -0
  127. package/dist/internal/transaction-execution-kernel.d.mts +38 -0
  128. package/dist/internal/transaction-execution-kernel.mjs +116 -0
  129. package/dist/internal/transaction-execution-kernel.mjs.map +1 -0
  130. package/dist/internal/transaction-kernel.d.mts +77 -0
  131. package/dist/internal/transaction-kernel.mjs +260 -0
  132. package/dist/internal/transaction-kernel.mjs.map +1 -0
  133. package/package.json +1 -0
  134. package/src/Adapter.ts +291 -0
  135. package/src/index.ts +6 -0
  136. package/src/internal/PasswordPreparedPostconditions.ts +30 -0
  137. package/src/internal/configuration.ts +282 -0
  138. package/src/internal/email-kernel.ts +1535 -0
  139. package/src/internal/mapping-error.ts +36 -0
  140. package/src/internal/models/common.ts +11 -0
  141. package/src/internal/models/email-model.ts +559 -0
  142. package/src/internal/models/passkey-model.ts +390 -0
  143. package/src/internal/models/passkey-registration-ceremony-model.ts +72 -0
  144. package/src/internal/models/passkey-write-model.ts +297 -0
  145. package/src/internal/models/password-model.ts +513 -0
  146. package/src/internal/models/proof-model.ts +424 -0
  147. package/src/internal/models/session-model.ts +375 -0
  148. package/src/internal/models/step-up-model.ts +135 -0
  149. package/src/internal/oauth-app.ts +82 -0
  150. package/src/internal/oauth-server.ts +89 -0
  151. package/src/internal/passkey/admission.ts +361 -0
  152. package/src/internal/passkey/credentials.ts +511 -0
  153. package/src/internal/passkey/enrollment.ts +326 -0
  154. package/src/internal/passkey/flow.ts +1062 -0
  155. package/src/internal/passkey/management.ts +421 -0
  156. package/src/internal/passkey/registration-ceremony.ts +283 -0
  157. package/src/internal/passkey/registration-custody.ts +91 -0
  158. package/src/internal/passkey/registration-write.ts +426 -0
  159. package/src/internal/passkey/state.ts +353 -0
  160. package/src/internal/passkey/target.ts +586 -0
  161. package/src/internal/passkey/write-state.ts +681 -0
  162. package/src/internal/passkey/write-target.ts +452 -0
  163. package/src/internal/passkey-kernel.ts +104 -0
  164. package/src/internal/passkeys.ts +606 -0
  165. package/src/internal/password-kernel.ts +2124 -0
  166. package/src/internal/persistence.ts +588 -0
  167. package/src/internal/phone-kernel.ts +846 -0
  168. package/src/internal/proof-kernel.ts +1954 -0
  169. package/src/internal/query-operations.ts +81 -0
  170. package/src/internal/registration-contract.ts +22 -0
  171. package/src/internal/registration.ts +161 -0
  172. package/src/internal/session-kernel.ts +2217 -0
  173. package/src/internal/sql-persistence.ts +45 -0
  174. package/src/internal/sql-query.ts +298 -0
  175. package/src/internal/sql-table.ts +104 -0
  176. package/src/internal/standalone.ts +30 -0
  177. package/src/internal/step-up-state.ts +139 -0
  178. package/src/internal/storage-mapping.ts +708 -0
  179. package/src/internal/storage-tables.ts +408 -0
  180. package/src/internal/storage-validation.ts +117 -0
  181. package/src/internal/transaction-execution-kernel.ts +313 -0
  182. package/src/internal/transaction-kernel.ts +593 -0
@@ -0,0 +1,326 @@
1
+ /* oxlint-disable no-explicit-any -- existing mapped-row bridge; public adapters preserve table, ID, and Effect types. */
2
+
3
+ import {
4
+ type PasskeyCeremony,
5
+ PasskeyCeremony as Ceremony,
6
+ type PasskeyManagementPersistence,
7
+ } from "@yielded/auth/Passkey";
8
+ import { Effect } from "effect";
9
+
10
+ import type { QueryOperations } from "../query-operations";
11
+ import type { makeTransactionKernel } from "../transaction-kernel";
12
+ import type { makePasskeyAdmissionKernel } from "./admission";
13
+ import type { makePasskeyCredentialsKernel } from "./credentials";
14
+ import type { makePasskeyFlowKernel } from "./flow";
15
+ import { CurrentPasskeyTransaction } from "./state";
16
+ import type { makePasskeyStateKernel } from "./state";
17
+ import type { WriteSubject, makePasskeyWriteStateKernel } from "./write-state";
18
+
19
+ type Issue = Parameters<PasskeyManagementPersistence["Service"]["issueEnrollment"]>[0];
20
+
21
+ type Complete = Parameters<PasskeyManagementPersistence["Service"]["completeEnrollment"]>[0];
22
+
23
+ export const makePasskeyEnrollmentKernel = (
24
+ operations: QueryOperations,
25
+ admission: Pick<
26
+ ReturnType<typeof makePasskeyAdmissionKernel>,
27
+ "lockAdmission" | "readCharges" | "guardChargeSet"
28
+ >,
29
+ credentials: Pick<
30
+ ReturnType<typeof makePasskeyCredentialsKernel>,
31
+ "readModule" | "readPolicyGuards"
32
+ >,
33
+ flow: Pick<
34
+ ReturnType<typeof makePasskeyFlowKernel>,
35
+ | "compatiblePolicy"
36
+ | "exactClaim"
37
+ | "issueAssertion"
38
+ | "liveCondition"
39
+ | "readFlow"
40
+ | "terminalFlow"
41
+ >,
42
+ state: Pick<
43
+ ReturnType<typeof makePasskeyStateKernel>,
44
+ "equal" | "handleKey" | "invariant" | "sameRevision" | "credentialKey"
45
+ >,
46
+ writeState: Pick<
47
+ ReturnType<typeof makePasskeyWriteStateKernel>,
48
+ | "authorizeAction"
49
+ | "credentialRows"
50
+ | "currentSubject"
51
+ | "enrollmentDigest"
52
+ | "digest"
53
+ | "insertCredential"
54
+ | "managementPolicy"
55
+ | "validRegistration"
56
+ >,
57
+ transactions: Pick<ReturnType<typeof makeTransactionKernel>, "both">,
58
+ ) => {
59
+ const { sql } = operations;
60
+ const { lockAdmission, readCharges, guardChargeSet } = admission;
61
+ const { readModule, readPolicyGuards } = credentials;
62
+
63
+ const { compatiblePolicy, exactClaim, issueAssertion, liveCondition, readFlow, terminalFlow } =
64
+ flow;
65
+
66
+ const { equal, handleKey, sameRevision, credentialKey: credentialKeyFor } = state;
67
+ const invariant: (value: unknown) => asserts value = state.invariant;
68
+
69
+ const {
70
+ authorizeAction,
71
+ credentialRows,
72
+ currentSubject,
73
+ enrollmentDigest,
74
+ digest,
75
+ insertCredential,
76
+ managementPolicy,
77
+ validRegistration,
78
+ } = writeState;
79
+
80
+ const { both } = transactions;
81
+
82
+ const enrollmentHandle = Effect.fn("passkey.enrollmentHandle")(function* (
83
+ mapping: any,
84
+ subject: WriteSubject,
85
+ ceremony: PasskeyCeremony,
86
+ create: boolean,
87
+ ) {
88
+ const owner = yield* CurrentPasskeyTransaction;
89
+
90
+ invariant(ceremony.context._tag === "Enrollment");
91
+ const table = mapping.read.handleOwnership;
92
+
93
+ const where = equal(table.table, {
94
+ [table.rpId]: ceremony.profile.rpId,
95
+ [table.subjectId]: subject.nativeId,
96
+ });
97
+
98
+ const found = yield* owner.read(table.table, where, { limit: 1 });
99
+ const row = found.rows[0];
100
+ const hashed = handleKey(ceremony.profile.rpId, ceremony.context.userHandle);
101
+
102
+ if (row !== undefined)
103
+ return (
104
+ table.isOwnedState(row[table.state]) &&
105
+ row[table.handleKey] === hashed &&
106
+ row[table.userHandle] === ceremony.context.userHandle &&
107
+ mapping.read.subjectIds.equals(table.decodeSubjectId(row), subject.nativeId)
108
+ );
109
+ if (!create) return false;
110
+
111
+ const absent = yield* owner.read(
112
+ table.table,
113
+ equal(table.table, { [table.handleKey]: hashed }),
114
+ {
115
+ limit: 1,
116
+ },
117
+ );
118
+
119
+ if (absent.rows.length !== 0) return false;
120
+
121
+ const inserted = yield* owner.insert(
122
+ table.table,
123
+ {
124
+ ...mapping.write.handleOwnership.encodeInsert({
125
+ subjectId: subject.nativeId,
126
+ rpId: ceremony.profile.rpId,
127
+ userHandle: ceremony.context.userHandle,
128
+ marker: owner.marker,
129
+ }),
130
+ [table.handleKey]: hashed,
131
+ [table.rpId]: ceremony.profile.rpId,
132
+ [table.userHandle]: ceremony.context.userHandle,
133
+ [table.subjectId]: subject.nativeId,
134
+ [table.state]: mapping.write.handleOwnership.ownedState,
135
+ [table.version]: owner.marker,
136
+ [table.reservationId]: null,
137
+ },
138
+ { [table.handleKey]: hashed },
139
+ );
140
+
141
+ found.rows = inserted.rows;
142
+ absent.rows = inserted.rows;
143
+
144
+ return true;
145
+ });
146
+
147
+ const capCondition = (mapping: any, subject: WriteSubject, maximum: number, addition: number) => {
148
+ const table = mapping.read.credential;
149
+
150
+ return sql`(select count(*) from ${table.table} where ${both(equal(table.table, { [table.subjectId]: subject.nativeId }), table.activeCondition)}) + ${addition} <= ${maximum}`;
151
+ };
152
+
153
+ const issueEnrollment = Effect.fn("passkey.issueEnrollment")(function* (
154
+ mapping: any,
155
+ input: Issue,
156
+ ) {
157
+ const owner = yield* CurrentPasskeyTransaction;
158
+ const ceremony = input.ceremony;
159
+
160
+ if (
161
+ ceremony.context._tag !== "Enrollment" ||
162
+ ceremony.purpose !== "enrollment" ||
163
+ ceremony.moduleId !== mapping.moduleId
164
+ )
165
+ return { _tag: "Rejected" } as const;
166
+ const current = yield* readModule(mapping);
167
+
168
+ if (current === undefined || !compatiblePolicy(ceremony, input.policy, current))
169
+ return { _tag: "Rejected" } as const;
170
+ yield* lockAdmission(mapping);
171
+ const subject = yield* currentSubject(mapping, ceremony.context.revision.subjectId);
172
+
173
+ if (subject === undefined || !sameRevision(subject.revision, ceremony.context.revision))
174
+ return { _tag: "Rejected" } as const;
175
+ yield* readPolicyGuards(mapping);
176
+ const policy = managementPolicy(mapping, subject);
177
+ const maximum = Math.min(policy.maximumCredentials, input.management.maximumCredentials);
178
+ const rows = yield* credentialRows(mapping, subject, ceremony.profile.rpId);
179
+ const ids = rows.map((row) => row[mapping.read.credential.protocolCredentialId]);
180
+
181
+ if (
182
+ ids.length !== ceremony.allowedCredentials.length ||
183
+ !ids.every((id) => ceremony.allowedCredentials.some((item) => item.id === id)) ||
184
+ !(yield* owner.check(capCondition(mapping, subject, maximum, 1)))
185
+ )
186
+ return { _tag: "Rejected" } as const;
187
+ if (
188
+ !(yield* authorizeAction(
189
+ mapping,
190
+ subject,
191
+ input.authorization,
192
+ {
193
+ action: "enroll-begin",
194
+ commandId: ceremony.commandId,
195
+ flowId: ceremony.flowId,
196
+ bindingDigest: enrollmentDigest(ceremony),
197
+ revision: ceremony.context.revision,
198
+ },
199
+ {
200
+ ...policy,
201
+ maximumEvidenceAgeMillis: Math.min(
202
+ policy.maximumEvidenceAgeMillis,
203
+ input.management.maximumEvidenceAgeMillis,
204
+ ),
205
+ },
206
+ ))
207
+ )
208
+ return { _tag: "Rejected" } as const;
209
+ invariant(
210
+ digest(Ceremony.fields.context, ceremony.context) ===
211
+ digest(Ceremony.fields.context, {
212
+ ...ceremony.context,
213
+ authorization: input.authorization,
214
+ }),
215
+ );
216
+ const issued = yield* issueAssertion(mapping, ceremony, input.policy);
217
+
218
+ if (issued._tag !== "Issued") return issued;
219
+ invariant(yield* enrollmentHandle(mapping, subject, ceremony, true));
220
+ owner.postconditions.push(capCondition(mapping, subject, maximum, 1));
221
+
222
+ return issued;
223
+ });
224
+
225
+ const completeEnrollment = Effect.fn("passkey.completeEnrollment")(function* (
226
+ mapping: any,
227
+ input: Complete,
228
+ ) {
229
+ const owner = yield* CurrentPasskeyTransaction;
230
+ const ceremony = input.claim.ceremony;
231
+
232
+ if (
233
+ ceremony.context._tag !== "Enrollment" ||
234
+ ceremony.purpose !== "enrollment" ||
235
+ ceremony.moduleId !== mapping.moduleId
236
+ )
237
+ return { _tag: "Rejected" } as const;
238
+ const current = yield* readModule(mapping);
239
+
240
+ if (current === undefined) return { _tag: "Rejected" } as const;
241
+ yield* lockAdmission(mapping);
242
+ const subject = yield* currentSubject(mapping, ceremony.context.revision.subjectId);
243
+
244
+ yield* readPolicyGuards(mapping);
245
+ const read = yield* readFlow(mapping, ceremony.flowId);
246
+
247
+ if (read === undefined || !exactClaim(read, input.claim)) return { _tag: "Rejected" } as const;
248
+
249
+ const reject = Effect.gen(function* () {
250
+ yield* terminalFlow(mapping, read, "Rejected");
251
+
252
+ return { _tag: "Rejected" } as const;
253
+ });
254
+
255
+ if (
256
+ subject === undefined ||
257
+ !sameRevision(subject.revision, ceremony.context.revision) ||
258
+ !compatiblePolicy(ceremony, read.policy, current) ||
259
+ !validRegistration(ceremony, input.verified) ||
260
+ !(yield* owner.check(liveCondition(mapping, ceremony, input.claim)))
261
+ )
262
+ return yield* reject;
263
+ const policy = managementPolicy(mapping, subject);
264
+ const maximum = Math.min(policy.maximumCredentials, input.management.maximumCredentials);
265
+
266
+ if (
267
+ !(yield* owner.check(capCondition(mapping, subject, maximum, 1))) ||
268
+ !(yield* enrollmentHandle(mapping, subject, ceremony, false))
269
+ )
270
+ return yield* reject;
271
+ if (
272
+ !(yield* authorizeAction(
273
+ mapping,
274
+ subject,
275
+ input.authorization,
276
+ {
277
+ action: "enroll-complete",
278
+ commandId: ceremony.commandId,
279
+ flowId: ceremony.flowId,
280
+ bindingDigest: digest(Ceremony, ceremony),
281
+ revision: ceremony.context.revision,
282
+ },
283
+ {
284
+ ...policy,
285
+ maximumEvidenceAgeMillis: Math.min(
286
+ policy.maximumEvidenceAgeMillis,
287
+ input.management.maximumEvidenceAgeMillis,
288
+ ),
289
+ },
290
+ ceremony.context.authorization.requirement,
291
+ ))
292
+ )
293
+ return yield* reject;
294
+ const tuple = mapping.read.credentialOwnership;
295
+ // Credential ownership is RP-global, including retained registration custody.
296
+ const key = credentialKeyFor(ceremony.profile.rpId, input.verified.protocolCredentialId);
297
+
298
+ if (
299
+ (yield* owner.read(tuple.table, equal(tuple.table, { [tuple.credentialKey]: key }), {
300
+ limit: 1,
301
+ observe: false,
302
+ })).rows.length !== 0
303
+ )
304
+ return yield* reject;
305
+ yield* readCharges(mapping, ceremony, read.policy, subject.subjectId);
306
+ yield* guardChargeSet(mapping, ceremony, subject.subjectId);
307
+
308
+ const safe = yield* insertCredential(
309
+ mapping,
310
+ subject,
311
+ ceremony,
312
+ input.verified,
313
+ yield* owner.now(mapping.clock),
314
+ );
315
+
316
+ yield* terminalFlow(mapping, read, "Verified");
317
+ owner.postconditions.push(
318
+ liveCondition(mapping, ceremony, input.claim),
319
+ capCondition(mapping, subject, maximum, 0),
320
+ );
321
+
322
+ return { _tag: "Enrolled", credential: safe } as const;
323
+ });
324
+
325
+ return { enrollmentHandle, issueEnrollment, completeEnrollment };
326
+ };