effect 4.1.0 → 4.1.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.
Files changed (354) hide show
  1. package/README.md +30 -0
  2. package/THIRD_PARTY_NOTICES.md +62 -0
  3. package/dist/BigDecimal.d.ts +139 -7
  4. package/dist/BigDecimal.d.ts.map +1 -1
  5. package/dist/BigDecimal.js +83 -14
  6. package/dist/BigDecimal.js.map +1 -1
  7. package/dist/Equal.d.ts +2 -2
  8. package/dist/Equal.d.ts.map +1 -1
  9. package/dist/Equal.js +3 -3
  10. package/dist/Equal.js.map +1 -1
  11. package/dist/Hash.d.ts +3 -2
  12. package/dist/Hash.d.ts.map +1 -1
  13. package/dist/Hash.js +5 -4
  14. package/dist/Hash.js.map +1 -1
  15. package/dist/Iterable.d.ts.map +1 -1
  16. package/dist/Iterable.js +11 -8
  17. package/dist/Iterable.js.map +1 -1
  18. package/dist/Path.d.ts.map +1 -1
  19. package/dist/Path.js +5 -1
  20. package/dist/Path.js.map +1 -1
  21. package/dist/Pool.d.ts.map +1 -1
  22. package/dist/Pool.js +2 -1
  23. package/dist/Pool.js.map +1 -1
  24. package/dist/Schema.d.ts +2 -1
  25. package/dist/Schema.d.ts.map +1 -1
  26. package/dist/Schema.js +2 -1
  27. package/dist/Schema.js.map +1 -1
  28. package/dist/SchemaRepresentation.d.ts +4 -2
  29. package/dist/SchemaRepresentation.d.ts.map +1 -1
  30. package/dist/SchemaRepresentation.js +4 -2
  31. package/dist/SchemaRepresentation.js.map +1 -1
  32. package/dist/SchemaTransformation.d.ts +2 -2
  33. package/dist/SchemaTransformation.js +2 -2
  34. package/dist/ai/McpClient.d.ts +171 -19
  35. package/dist/ai/McpClient.d.ts.map +1 -1
  36. package/dist/ai/McpClient.js +277 -66
  37. package/dist/ai/McpClient.js.map +1 -1
  38. package/dist/ai/McpSchema.d.ts +108 -0
  39. package/dist/ai/McpSchema.d.ts.map +1 -1
  40. package/dist/ai/McpSchema.js +99 -0
  41. package/dist/ai/McpSchema.js.map +1 -1
  42. package/dist/ai/McpServer.d.ts +120 -6
  43. package/dist/ai/McpServer.d.ts.map +1 -1
  44. package/dist/ai/McpServer.js +215 -34
  45. package/dist/ai/McpServer.js.map +1 -1
  46. package/dist/ai/Prompt.d.ts.map +1 -1
  47. package/dist/ai/Prompt.js +20 -11
  48. package/dist/ai/Prompt.js.map +1 -1
  49. package/dist/ai/Tool.d.ts.map +1 -1
  50. package/dist/ai/Tool.js +5 -7
  51. package/dist/ai/Tool.js.map +1 -1
  52. package/dist/ai/internal/mcpCore.js +72 -3
  53. package/dist/ai/internal/mcpCore.js.map +1 -1
  54. package/dist/ai/internal/mcpProtocol/v2024_11_05.js +3 -1
  55. package/dist/ai/internal/mcpProtocol/v2024_11_05.js.map +1 -1
  56. package/dist/ai/internal/mcpProtocol/v2025_03_26.js +3 -1
  57. package/dist/ai/internal/mcpProtocol/v2025_03_26.js.map +1 -1
  58. package/dist/ai/internal/mcpProtocol/v2025_06_18.js +3 -1
  59. package/dist/ai/internal/mcpProtocol/v2025_06_18.js.map +1 -1
  60. package/dist/ai/internal/mcpProtocol/v2025_11_25.js +3 -1
  61. package/dist/ai/internal/mcpProtocol/v2025_11_25.js.map +1 -1
  62. package/dist/ai/internal/mcpProtocol/v2026_07_28.js +42 -7
  63. package/dist/ai/internal/mcpProtocol/v2026_07_28.js.map +1 -1
  64. package/dist/ai/internal/mcpProtocol.js +12 -0
  65. package/dist/ai/internal/mcpProtocol.js.map +1 -1
  66. package/dist/ai/internal/mcpRuntime.js +3 -3
  67. package/dist/ai/internal/mcpRuntime.js.map +1 -1
  68. package/dist/ai/internal/mcpSkills.d.ts +2 -0
  69. package/dist/ai/internal/mcpSkills.d.ts.map +1 -0
  70. package/dist/ai/internal/mcpSkills.js +428 -0
  71. package/dist/ai/internal/mcpSkills.js.map +1 -0
  72. package/dist/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
  73. package/dist/cluster/ClusterWorkflowEngine.js +15 -5
  74. package/dist/cluster/ClusterWorkflowEngine.js.map +1 -1
  75. package/dist/cluster/Reply.d.ts +1 -1
  76. package/dist/cluster/Reply.d.ts.map +1 -1
  77. package/dist/cluster/Reply.js.map +1 -1
  78. package/dist/cluster/Sharding.d.ts.map +1 -1
  79. package/dist/cluster/Sharding.js +4 -1
  80. package/dist/cluster/Sharding.js.map +1 -1
  81. package/dist/cluster/internal/entityManager.js +15 -7
  82. package/dist/cluster/internal/entityManager.js.map +1 -1
  83. package/dist/crypto/Aead.d.ts +2 -1
  84. package/dist/crypto/Aead.d.ts.map +1 -1
  85. package/dist/crypto/Aead.js +2 -2
  86. package/dist/crypto/Aead.js.map +1 -1
  87. package/dist/crypto/Cipher.d.ts +2 -1
  88. package/dist/crypto/Cipher.d.ts.map +1 -1
  89. package/dist/crypto/Cipher.js +2 -2
  90. package/dist/crypto/Cipher.js.map +1 -1
  91. package/dist/crypto/KeyAgreement.d.ts +3 -2
  92. package/dist/crypto/KeyAgreement.d.ts.map +1 -1
  93. package/dist/crypto/KeyAgreement.js +2 -2
  94. package/dist/crypto/KeyAgreement.js.map +1 -1
  95. package/dist/crypto/KeyFormat.d.ts +33 -1
  96. package/dist/crypto/KeyFormat.d.ts.map +1 -1
  97. package/dist/crypto/KeyFormat.js.map +1 -1
  98. package/dist/crypto/KeyWrap.d.ts +4 -3
  99. package/dist/crypto/KeyWrap.d.ts.map +1 -1
  100. package/dist/crypto/KeyWrap.js +2 -2
  101. package/dist/crypto/KeyWrap.js.map +1 -1
  102. package/dist/crypto/Mac.d.ts +2 -1
  103. package/dist/crypto/Mac.d.ts.map +1 -1
  104. package/dist/crypto/Mac.js +2 -2
  105. package/dist/crypto/Mac.js.map +1 -1
  106. package/dist/crypto/Signature.d.ts +3 -2
  107. package/dist/crypto/Signature.d.ts.map +1 -1
  108. package/dist/crypto/Signature.js +2 -2
  109. package/dist/crypto/Signature.js.map +1 -1
  110. package/dist/crypto/WebCrypto.d.ts +19 -0
  111. package/dist/crypto/WebCrypto.d.ts.map +1 -1
  112. package/dist/crypto/WebCrypto.js.map +1 -1
  113. package/dist/crypto/internal/asymmetric.js.map +1 -1
  114. package/dist/crypto/internal/key.js +3 -2
  115. package/dist/crypto/internal/key.js.map +1 -1
  116. package/dist/encoding/Base64Url.d.ts.map +1 -1
  117. package/dist/encoding/Base64Url.js +26 -3
  118. package/dist/encoding/Base64Url.js.map +1 -1
  119. package/dist/encoding/Ndjson.d.ts +5 -4
  120. package/dist/encoding/Ndjson.d.ts.map +1 -1
  121. package/dist/encoding/Ndjson.js +6 -5
  122. package/dist/encoding/Ndjson.js.map +1 -1
  123. package/dist/encoding/Sse.d.ts +3 -1
  124. package/dist/encoding/Sse.d.ts.map +1 -1
  125. package/dist/encoding/Sse.js +5 -3
  126. package/dist/encoding/Sse.js.map +1 -1
  127. package/dist/eventlog/EventLog.d.ts +7 -0
  128. package/dist/eventlog/EventLog.d.ts.map +1 -1
  129. package/dist/eventlog/EventLog.js +7 -0
  130. package/dist/eventlog/EventLog.js.map +1 -1
  131. package/dist/eventlog/EventLogServer.d.ts +7 -1
  132. package/dist/eventlog/EventLogServer.d.ts.map +1 -1
  133. package/dist/eventlog/EventLogServer.js +27 -12
  134. package/dist/eventlog/EventLogServer.js.map +1 -1
  135. package/dist/eventlog/EventLogServerEncrypted.d.ts +10 -0
  136. package/dist/eventlog/EventLogServerEncrypted.d.ts.map +1 -1
  137. package/dist/eventlog/EventLogServerEncrypted.js.map +1 -1
  138. package/dist/eventlog/EventLogServerUnencrypted.d.ts +17 -0
  139. package/dist/eventlog/EventLogServerUnencrypted.d.ts.map +1 -1
  140. package/dist/eventlog/EventLogServerUnencrypted.js +14 -2
  141. package/dist/eventlog/EventLogServerUnencrypted.js.map +1 -1
  142. package/dist/eventlog/EventLogSessionAuth.d.ts +21 -0
  143. package/dist/eventlog/EventLogSessionAuth.d.ts.map +1 -1
  144. package/dist/eventlog/EventLogSessionAuth.js +22 -0
  145. package/dist/eventlog/EventLogSessionAuth.js.map +1 -1
  146. package/dist/graphql/GraphQLClient.d.ts +3 -1
  147. package/dist/graphql/GraphQLClient.d.ts.map +1 -1
  148. package/dist/graphql/GraphQLClient.js.map +1 -1
  149. package/dist/graphql/GraphQLClientError.d.ts +14 -9
  150. package/dist/graphql/GraphQLClientError.d.ts.map +1 -1
  151. package/dist/graphql/GraphQLClientError.js +14 -21
  152. package/dist/graphql/GraphQLClientError.js.map +1 -1
  153. package/dist/graphql/GraphQLProtocol.d.ts +12 -3
  154. package/dist/graphql/GraphQLProtocol.d.ts.map +1 -1
  155. package/dist/graphql/GraphQLProtocol.js +4 -1
  156. package/dist/graphql/GraphQLProtocol.js.map +1 -1
  157. package/dist/http/HttpClient.d.ts +189 -7
  158. package/dist/http/HttpClient.d.ts.map +1 -1
  159. package/dist/http/HttpClient.js +75 -2
  160. package/dist/http/HttpClient.js.map +1 -1
  161. package/dist/http/HttpServerRequest.d.ts.map +1 -1
  162. package/dist/http/HttpServerRequest.js +38 -22
  163. package/dist/http/HttpServerRequest.js.map +1 -1
  164. package/dist/http/internal/mimeTypes.d.ts +4 -1
  165. package/dist/http/internal/mimeTypes.d.ts.map +1 -1
  166. package/dist/http/internal/mimeTypes.js +4 -1
  167. package/dist/http/internal/mimeTypes.js.map +1 -1
  168. package/dist/http-api/HttpApiSchema.js +1 -1
  169. package/dist/http-api/HttpApiSchema.js.map +1 -1
  170. package/dist/internal/dateTime.js +49 -24
  171. package/dist/internal/dateTime.js.map +1 -1
  172. package/dist/internal/effect.js +10 -0
  173. package/dist/internal/effect.js.map +1 -1
  174. package/dist/internal/glob.js +234 -56
  175. package/dist/internal/glob.js.map +1 -1
  176. package/dist/internal/graphql.js +21 -7
  177. package/dist/internal/graphql.js.map +1 -1
  178. package/dist/internal/graphqlHeaders.d.ts +7 -0
  179. package/dist/internal/graphqlHeaders.d.ts.map +1 -1
  180. package/dist/internal/graphqlHeaders.js +11 -0
  181. package/dist/internal/graphqlHeaders.js.map +1 -1
  182. package/dist/internal/request.js +29 -42
  183. package/dist/internal/request.js.map +1 -1
  184. package/dist/internal/schema/fromJsonSchemaDocument.js +7 -8
  185. package/dist/internal/schema/fromJsonSchemaDocument.js.map +1 -1
  186. package/dist/internal/version.js +1 -1
  187. package/dist/jose/JoseError.d.ts +285 -0
  188. package/dist/jose/JoseError.d.ts.map +1 -0
  189. package/dist/jose/JoseError.js +269 -0
  190. package/dist/jose/JoseError.js.map +1 -0
  191. package/dist/jose/Jwe.d.ts +128 -0
  192. package/dist/jose/Jwe.d.ts.map +1 -0
  193. package/dist/jose/Jwe.js +134 -0
  194. package/dist/jose/Jwe.js.map +1 -0
  195. package/dist/jose/Jwk.d.ts +597 -0
  196. package/dist/jose/Jwk.d.ts.map +1 -0
  197. package/dist/jose/Jwk.js +503 -0
  198. package/dist/jose/Jwk.js.map +1 -0
  199. package/dist/jose/Jwks.d.ts +236 -0
  200. package/dist/jose/Jwks.d.ts.map +1 -0
  201. package/dist/jose/Jwks.js +306 -0
  202. package/dist/jose/Jwks.js.map +1 -0
  203. package/dist/jose/Jws.d.ts +177 -0
  204. package/dist/jose/Jws.d.ts.map +1 -0
  205. package/dist/jose/Jws.js +183 -0
  206. package/dist/jose/Jws.js.map +1 -0
  207. package/dist/jose/Jwt.d.ts +270 -0
  208. package/dist/jose/Jwt.d.ts.map +1 -0
  209. package/dist/jose/Jwt.js +293 -0
  210. package/dist/jose/Jwt.js.map +1 -0
  211. package/dist/jose/index.d.ts +51 -0
  212. package/dist/jose/index.d.ts.map +1 -0
  213. package/dist/jose/index.js +52 -0
  214. package/dist/jose/index.js.map +1 -0
  215. package/dist/jose/internal/compact.d.ts +2 -0
  216. package/dist/jose/internal/compact.d.ts.map +1 -0
  217. package/dist/jose/internal/compact.js +179 -0
  218. package/dist/jose/internal/compact.js.map +1 -0
  219. package/dist/net/Host.d.ts.map +1 -1
  220. package/dist/net/Host.js +5 -10
  221. package/dist/net/Host.js.map +1 -1
  222. package/dist/observability/OtlpSerialization.d.ts +1 -1
  223. package/dist/observability/OtlpSerialization.d.ts.map +1 -1
  224. package/dist/observability/OtlpSerialization.js +10 -4
  225. package/dist/observability/OtlpSerialization.js.map +1 -1
  226. package/dist/observability/internal/otlpProtobuf.js +3 -3
  227. package/dist/observability/internal/otlpProtobuf.js.map +1 -1
  228. package/dist/observability/internal/protobuf.js +6 -0
  229. package/dist/observability/internal/protobuf.js.map +1 -1
  230. package/dist/persistence/CircuitBreaker.d.ts +98 -38
  231. package/dist/persistence/CircuitBreaker.d.ts.map +1 -1
  232. package/dist/persistence/CircuitBreaker.js +67 -29
  233. package/dist/persistence/CircuitBreaker.js.map +1 -1
  234. package/dist/persistence/RateLimiter.d.ts +8 -0
  235. package/dist/persistence/RateLimiter.d.ts.map +1 -1
  236. package/dist/persistence/RateLimiter.js +63 -12
  237. package/dist/persistence/RateLimiter.js.map +1 -1
  238. package/dist/persistence/index.d.ts +1 -1
  239. package/dist/persistence/index.js +1 -1
  240. package/dist/reactivity/AtomRegistry.js +17 -7
  241. package/dist/reactivity/AtomRegistry.js.map +1 -1
  242. package/dist/rpc/Rpc.d.ts +1 -1
  243. package/dist/rpc/Rpc.d.ts.map +1 -1
  244. package/dist/rpc/Rpc.js.map +1 -1
  245. package/dist/rpc/RpcClient.d.ts +14 -2
  246. package/dist/rpc/RpcClient.d.ts.map +1 -1
  247. package/dist/rpc/RpcClient.js +38 -21
  248. package/dist/rpc/RpcClient.js.map +1 -1
  249. package/dist/rpc/RpcMessage.d.ts +3 -2
  250. package/dist/rpc/RpcMessage.d.ts.map +1 -1
  251. package/dist/rpc/RpcMessage.js.map +1 -1
  252. package/dist/sql/Migrator.d.ts +9 -1
  253. package/dist/sql/Migrator.d.ts.map +1 -1
  254. package/dist/sql/Migrator.js +18 -6
  255. package/dist/sql/Migrator.js.map +1 -1
  256. package/dist/sql/SqlClient.d.ts +38 -3
  257. package/dist/sql/SqlClient.d.ts.map +1 -1
  258. package/dist/sql/SqlClient.js +4 -3
  259. package/dist/sql/SqlClient.js.map +1 -1
  260. package/dist/sql/Statement.d.ts +41 -3
  261. package/dist/sql/Statement.d.ts.map +1 -1
  262. package/dist/sql/Statement.js +24 -20
  263. package/dist/sql/Statement.js.map +1 -1
  264. package/dist/workflow/Workflow.d.ts +4 -0
  265. package/dist/workflow/Workflow.d.ts.map +1 -1
  266. package/dist/workflow/Workflow.js.map +1 -1
  267. package/dist/workflow/WorkflowEngine.d.ts.map +1 -1
  268. package/dist/workflow/WorkflowEngine.js +5 -0
  269. package/dist/workflow/WorkflowEngine.js.map +1 -1
  270. package/package.json +5 -1
  271. package/src/BigDecimal.ts +170 -16
  272. package/src/Equal.ts +3 -3
  273. package/src/Hash.ts +5 -4
  274. package/src/Iterable.ts +8 -5
  275. package/src/Path.ts +6 -1
  276. package/src/Pool.ts +2 -1
  277. package/src/Schema.ts +3 -2
  278. package/src/SchemaRepresentation.ts +4 -2
  279. package/src/SchemaTransformation.ts +2 -2
  280. package/src/ai/McpClient.ts +488 -91
  281. package/src/ai/McpSchema.ts +119 -0
  282. package/src/ai/McpServer.ts +261 -38
  283. package/src/ai/Prompt.ts +16 -11
  284. package/src/ai/Tool.ts +5 -10
  285. package/src/ai/internal/mcpCore.ts +165 -12
  286. package/src/ai/internal/mcpProtocol/v2024_11_05.ts +3 -1
  287. package/src/ai/internal/mcpProtocol/v2025_03_26.ts +3 -1
  288. package/src/ai/internal/mcpProtocol/v2025_06_18.ts +3 -1
  289. package/src/ai/internal/mcpProtocol/v2025_11_25.ts +3 -1
  290. package/src/ai/internal/mcpProtocol/v2026_07_28.ts +41 -7
  291. package/src/ai/internal/mcpProtocol.ts +15 -0
  292. package/src/ai/internal/mcpRuntime.ts +6 -3
  293. package/src/ai/internal/mcpSkills.ts +480 -0
  294. package/src/cluster/ClusterWorkflowEngine.ts +22 -8
  295. package/src/cluster/Reply.ts +1 -1
  296. package/src/cluster/Sharding.ts +4 -1
  297. package/src/cluster/internal/entityManager.ts +24 -10
  298. package/src/crypto/Aead.ts +3 -3
  299. package/src/crypto/Cipher.ts +3 -3
  300. package/src/crypto/KeyAgreement.ts +4 -4
  301. package/src/crypto/KeyFormat.ts +30 -1
  302. package/src/crypto/KeyWrap.ts +6 -6
  303. package/src/crypto/Mac.ts +3 -3
  304. package/src/crypto/Signature.ts +4 -4
  305. package/src/crypto/WebCrypto.ts +20 -0
  306. package/src/crypto/internal/asymmetric.ts +2 -1
  307. package/src/crypto/internal/key.ts +12 -5
  308. package/src/encoding/Base64Url.ts +27 -3
  309. package/src/encoding/Ndjson.ts +6 -5
  310. package/src/encoding/Sse.ts +5 -3
  311. package/src/eventlog/EventLog.ts +7 -0
  312. package/src/eventlog/EventLogServer.ts +31 -12
  313. package/src/eventlog/EventLogServerEncrypted.ts +5 -0
  314. package/src/eventlog/EventLogServerUnencrypted.ts +19 -2
  315. package/src/eventlog/EventLogSessionAuth.ts +26 -0
  316. package/src/graphql/GraphQLClient.ts +3 -1
  317. package/src/graphql/GraphQLClientError.ts +15 -22
  318. package/src/graphql/GraphQLProtocol.ts +12 -3
  319. package/src/http/HttpClient.ts +210 -11
  320. package/src/http/HttpServerRequest.ts +61 -23
  321. package/src/http/internal/mimeTypes.ts +4 -1
  322. package/src/http-api/HttpApiSchema.ts +1 -1
  323. package/src/internal/dateTime.ts +54 -24
  324. package/src/internal/effect.ts +6 -0
  325. package/src/internal/glob.ts +279 -63
  326. package/src/internal/graphql.ts +27 -9
  327. package/src/internal/graphqlHeaders.ts +13 -0
  328. package/src/internal/request.ts +51 -58
  329. package/src/internal/schema/fromJsonSchemaDocument.ts +11 -10
  330. package/src/internal/version.ts +1 -1
  331. package/src/jose/JoseError.ts +329 -0
  332. package/src/jose/Jwe.ts +196 -0
  333. package/src/jose/Jwk.ts +777 -0
  334. package/src/jose/Jwks.ts +418 -0
  335. package/src/jose/Jws.ts +312 -0
  336. package/src/jose/Jwt.ts +493 -0
  337. package/src/jose/index.ts +58 -0
  338. package/src/jose/internal/compact.ts +195 -0
  339. package/src/net/Host.ts +5 -10
  340. package/src/observability/OtlpSerialization.ts +13 -5
  341. package/src/observability/internal/otlpProtobuf.ts +6 -6
  342. package/src/observability/internal/protobuf.ts +10 -0
  343. package/src/persistence/CircuitBreaker.ts +113 -48
  344. package/src/persistence/RateLimiter.ts +84 -15
  345. package/src/persistence/index.ts +1 -1
  346. package/src/reactivity/AtomRegistry.ts +19 -8
  347. package/src/rpc/Rpc.ts +3 -1
  348. package/src/rpc/RpcClient.ts +44 -24
  349. package/src/rpc/RpcMessage.ts +3 -2
  350. package/src/sql/Migrator.ts +35 -18
  351. package/src/sql/SqlClient.ts +43 -8
  352. package/src/sql/Statement.ts +52 -22
  353. package/src/workflow/Workflow.ts +4 -0
  354. package/src/workflow/WorkflowEngine.ts +5 -0
@@ -35,7 +35,7 @@ import * as CryptoError from "./CryptoError.ts"
35
35
  import * as KeyModel from "./internal/key.ts"
36
36
  import * as internal from "./internal/webCrypto.ts"
37
37
  import * as SecureRandom from "./SecureRandom.ts"
38
- import { WebCrypto } from "./WebCrypto.ts"
38
+ import * as WebCrypto from "./WebCrypto.ts"
39
39
 
40
40
  const TypeId = "~effect/crypto/Cipher/Key" as const
41
41
 
@@ -217,7 +217,7 @@ export const layerWebCrypto: Layer.Layer<Cipher, never, SecureRandom.SecureRando
217
217
  Cipher,
218
218
  Effect.gen(function*() {
219
219
  const random = yield* SecureRandom.SecureRandom
220
- const crypto = yield* WebCrypto
220
+ const crypto = yield* WebCrypto.WebCrypto
221
221
  const cryptoKeyFor = KeyModel.makeCache((subtle, bytes, algorithm: Algorithm) =>
222
222
  subtle.importKey("raw", bytes as BufferSource, algorithm.mode, false, ["encrypt", "decrypt"])
223
223
  )
@@ -299,7 +299,7 @@ export const key = <A extends Algorithm>(
299
299
  */
300
300
  export const fromCryptoKey = <A extends Algorithm>(
301
301
  algorithm: A,
302
- cryptoKey: CryptoKey
302
+ cryptoKey: WebCrypto.CryptoKey
303
303
  ): Effect.Effect<Key<A>, CryptoError.CryptoError> =>
304
304
  KeyModel.fromCryptoKey("Cipher", "fromCryptoKey", KeyProto, algorithm, cryptoKey, {
305
305
  type: "secret",
@@ -43,7 +43,7 @@ import * as Asymmetric from "./internal/asymmetric.ts"
43
43
  import * as KeyModel from "./internal/key.ts"
44
44
  import * as internal from "./internal/webCrypto.ts"
45
45
  import * as SecureRandom from "./SecureRandom.ts"
46
- import { WebCrypto } from "./WebCrypto.ts"
46
+ import * as WebCrypto from "./WebCrypto.ts"
47
47
 
48
48
  const PrivateKeyTypeId = "~effect/crypto/KeyAgreement/PrivateKey" as const
49
49
  const PublicKeyTypeId = "~effect/crypto/KeyAgreement/PublicKey" as const
@@ -307,7 +307,7 @@ export const layerWebCrypto: Layer.Layer<KeyAgreement, never, SecureRandom.Secur
307
307
  KeyAgreement,
308
308
  Effect.gen(function*() {
309
309
  const random = yield* SecureRandom.SecureRandom
310
- const crypto = yield* WebCrypto
310
+ const crypto = yield* WebCrypto.WebCrypto
311
311
  const caches = Asymmetric.makeCaches(family)
312
312
  return KeyAgreement.of({
313
313
  generateKeyPair: <A extends Algorithm>(algorithm: A) =>
@@ -447,7 +447,7 @@ export const publicKey = <A extends Algorithm>(
447
447
  */
448
448
  export const privateKeyFromCryptoKey = <A extends Algorithm>(
449
449
  algorithm: A,
450
- cryptoKey: CryptoKey
450
+ cryptoKey: WebCrypto.CryptoKey
451
451
  ): Effect.Effect<PrivateKey<A>, CryptoError.CryptoError> =>
452
452
  Asymmetric.fromCryptoKey(family, "privateKeyFromCryptoKey", algorithm, "private", cryptoKey)
453
453
 
@@ -467,7 +467,7 @@ export const privateKeyFromCryptoKey = <A extends Algorithm>(
467
467
  */
468
468
  export const publicKeyFromCryptoKey = <A extends Algorithm>(
469
469
  algorithm: A,
470
- cryptoKey: CryptoKey
470
+ cryptoKey: WebCrypto.CryptoKey
471
471
  ): Effect.Effect<PublicKey<A>, CryptoError.CryptoError> =>
472
472
  Asymmetric.fromCryptoKey(family, "publicKeyFromCryptoKey", algorithm, "public", cryptoKey)
473
473
 
@@ -76,6 +76,35 @@ export type Role = "public" | "private" | "secret"
76
76
  */
77
77
  export type Direction = "decode" | "encode"
78
78
 
79
+ /**
80
+ * A JSON Web Key (RFC 7517) with the native Web Crypto field types and
81
+ * mutability, usable without DOM declarations.
82
+ *
83
+ * @stability unstable
84
+ * @category models
85
+ * @since 4.1.1
86
+ */
87
+ export interface JsonWebKey {
88
+ alg?: string
89
+ crv?: string
90
+ d?: string
91
+ dp?: string
92
+ dq?: string
93
+ e?: string
94
+ ext?: boolean
95
+ k?: string
96
+ key_ops?: Array<string>
97
+ kty?: string
98
+ n?: string
99
+ oth?: Array<{ d?: string; r?: string; t?: string }>
100
+ p?: string
101
+ q?: string
102
+ qi?: string
103
+ use?: string
104
+ x?: string
105
+ y?: string
106
+ }
107
+
79
108
  /**
80
109
  * A key format, such as {@link spki} or `pem(pkcs8)`.
81
110
  *
@@ -83,7 +112,7 @@ export type Direction = "decode" | "encode"
83
112
  *
84
113
  * `~tags` exists only in the types. It records the roles the format holds,
85
114
  * whether it can be read and written, and the type of its data: `Uint8Array`
86
- * for DER, `string` for PEM, and `JsonWebKey` for JWK.
115
+ * for DER, `string` for PEM, and {@link JsonWebKey} for JWK.
87
116
  *
88
117
  * @stability unstable
89
118
  * @category models
@@ -44,7 +44,7 @@ import * as internal from "./internal/webCrypto.ts"
44
44
  import type * as KeyFormat from "./KeyFormat.ts"
45
45
  import * as Mac from "./Mac.ts"
46
46
  import * as SecureRandom from "./SecureRandom.ts"
47
- import { WebCrypto } from "./WebCrypto.ts"
47
+ import * as WebCrypto from "./WebCrypto.ts"
48
48
 
49
49
  const KeyTypeId = "~effect/crypto/KeyWrap/Key" as const
50
50
  const PrivateKeyTypeId = "~effect/crypto/KeyWrap/PrivateKey" as const
@@ -417,7 +417,7 @@ export const layerWebCrypto: Layer.Layer<KeyWrap, never, SecureRandom.SecureRand
417
417
  KeyWrap,
418
418
  Effect.gen(function*() {
419
419
  const random = yield* SecureRandom.SecureRandom
420
- const crypto = yield* WebCrypto
420
+ const crypto = yield* WebCrypto.WebCrypto
421
421
  const kwCryptoKey = KeyModel.makeCache((subtle, bytes) =>
422
422
  subtle.importKey("raw", bytes as BufferSource, "AES-KW", false, ["wrapKey", "unwrapKey"])
423
423
  )
@@ -553,7 +553,7 @@ export const key = <A extends AesKw>(
553
553
  */
554
554
  export const fromCryptoKey = <A extends AesKw>(
555
555
  algorithm: A,
556
- cryptoKey: CryptoKey
556
+ cryptoKey: WebCrypto.CryptoKey
557
557
  ): Effect.Effect<Key<A>, CryptoError.CryptoError> =>
558
558
  KeyModel.fromCryptoKey("KeyWrap", "fromCryptoKey", KeyProto, algorithm, cryptoKey, {
559
559
  type: "secret",
@@ -570,7 +570,7 @@ const rsaFromCryptoKey = <K>(
570
570
  proto: object,
571
571
  algorithm: RsaOaep,
572
572
  role: Asymmetric.Role,
573
- cryptoKey: CryptoKey
573
+ cryptoKey: WebCrypto.CryptoKey
574
574
  ): Effect.Effect<K, CryptoError.CryptoError> =>
575
575
  KeyModel.fromCryptoKey("KeyWrap", method, proto, algorithm, cryptoKey, {
576
576
  type: role,
@@ -597,7 +597,7 @@ const rsaFromCryptoKey = <K>(
597
597
  */
598
598
  export const privateKeyFromCryptoKey = <A extends RsaOaep>(
599
599
  algorithm: A,
600
- cryptoKey: CryptoKey
600
+ cryptoKey: WebCrypto.CryptoKey
601
601
  ): Effect.Effect<PrivateKey<A>, CryptoError.CryptoError> =>
602
602
  rsaFromCryptoKey("privateKeyFromCryptoKey", PrivateKeyProto, algorithm, "private", cryptoKey)
603
603
 
@@ -617,7 +617,7 @@ export const privateKeyFromCryptoKey = <A extends RsaOaep>(
617
617
  */
618
618
  export const publicKeyFromCryptoKey = <A extends RsaOaep>(
619
619
  algorithm: A,
620
- cryptoKey: CryptoKey
620
+ cryptoKey: WebCrypto.CryptoKey
621
621
  ): Effect.Effect<PublicKey<A>, CryptoError.CryptoError> =>
622
622
  rsaFromCryptoKey("publicKeyFromCryptoKey", PublicKeyProto, algorithm, "public", cryptoKey)
623
623
 
package/src/crypto/Mac.ts CHANGED
@@ -23,7 +23,7 @@ import * as Digest from "./Digest.ts"
23
23
  import * as KeyModel from "./internal/key.ts"
24
24
  import * as internal from "./internal/webCrypto.ts"
25
25
  import * as SecureRandom from "./SecureRandom.ts"
26
- import { WebCrypto } from "./WebCrypto.ts"
26
+ import * as WebCrypto from "./WebCrypto.ts"
27
27
 
28
28
  const TypeId = "~effect/crypto/Mac/Key" as const
29
29
 
@@ -215,7 +215,7 @@ export const layerWebCryptoWith = (options: {
215
215
  Mac,
216
216
  Effect.gen(function*() {
217
217
  const random = yield* SecureRandom.SecureRandom
218
- const crypto = yield* WebCrypto
218
+ const crypto = yield* WebCrypto.WebCrypto
219
219
  const cryptoKeyFor = KeyModel.makeCache((subtle, bytes, algorithm: Algorithm) =>
220
220
  subtle.importKey("raw", bytes as BufferSource, importParams(algorithm), false, ["sign", "verify"])
221
221
  )
@@ -321,7 +321,7 @@ export const key = <A extends Algorithm>(
321
321
  */
322
322
  export const fromCryptoKey = <A extends Algorithm>(
323
323
  algorithm: A,
324
- cryptoKey: CryptoKey
324
+ cryptoKey: WebCrypto.CryptoKey
325
325
  ): Effect.Effect<Key<A>, CryptoError.CryptoError> =>
326
326
  KeyModel.fromCryptoKey("Mac", "fromCryptoKey", KeyProto, algorithm, cryptoKey, {
327
327
  type: "secret",
@@ -48,7 +48,7 @@ import * as Asymmetric from "./internal/asymmetric.ts"
48
48
  import * as KeyModel from "./internal/key.ts"
49
49
  import * as internal from "./internal/webCrypto.ts"
50
50
  import * as SecureRandom from "./SecureRandom.ts"
51
- import { WebCrypto } from "./WebCrypto.ts"
51
+ import * as WebCrypto from "./WebCrypto.ts"
52
52
 
53
53
  const PrivateKeyTypeId = "~effect/crypto/Signature/PrivateKey" as const
54
54
  const PublicKeyTypeId = "~effect/crypto/Signature/PublicKey" as const
@@ -513,7 +513,7 @@ export const layerWebCrypto: Layer.Layer<Signature, never, SecureRandom.SecureRa
513
513
  Signature,
514
514
  Effect.gen(function*() {
515
515
  const random = yield* SecureRandom.SecureRandom
516
- const crypto = yield* WebCrypto
516
+ const crypto = yield* WebCrypto.WebCrypto
517
517
  const caches = Asymmetric.makeCaches(family)
518
518
  return Signature.of({
519
519
  generateKeyPair: <A extends Algorithm>(algorithm: A) =>
@@ -630,7 +630,7 @@ export const publicKey = <
630
630
  */
631
631
  export const privateKeyFromCryptoKey = <A extends Algorithm>(
632
632
  algorithm: A,
633
- cryptoKey: CryptoKey
633
+ cryptoKey: WebCrypto.CryptoKey
634
634
  ): Effect.Effect<PrivateKey<A>, CryptoError.CryptoError> =>
635
635
  Asymmetric.fromCryptoKey(family, "privateKeyFromCryptoKey", algorithm, "private", cryptoKey)
636
636
 
@@ -650,7 +650,7 @@ export const privateKeyFromCryptoKey = <A extends Algorithm>(
650
650
  */
651
651
  export const publicKeyFromCryptoKey = <A extends Algorithm>(
652
652
  algorithm: A,
653
- cryptoKey: CryptoKey
653
+ cryptoKey: WebCrypto.CryptoKey
654
654
  ): Effect.Effect<PublicKey<A>, CryptoError.CryptoError> =>
655
655
  Asymmetric.fromCryptoKey(family, "publicKeyFromCryptoKey", algorithm, "public", cryptoKey)
656
656
 
@@ -20,6 +20,10 @@
20
20
  import * as Context from "../Context.ts"
21
21
  import * as Layer from "../Layer.ts"
22
22
 
23
+ // The type of `globalThis.crypto`, which, unlike the global `Crypto` type,
24
+ // every `@types/node` version declares.
25
+ type Crypto = typeof globalThis.crypto
26
+
23
27
  /**
24
28
  * Reference to the WebCrypto `Crypto` object, defaulting to
25
29
  * `globalThis.crypto`.
@@ -62,3 +66,19 @@ export const WebCrypto: Context.Reference<Crypto> = Context.Reference<Crypto>("e
62
66
  * @since 4.0.0
63
67
  */
64
68
  export const layer = (crypto: Crypto): Layer.Layer<never> => Layer.succeed(WebCrypto, crypto)
69
+
70
+ /**
71
+ * A WebCrypto `CryptoKey`, declared structurally so the `effect` declarations
72
+ * type-check without the DOM library and with any `@types/node` version.
73
+ * Keys from `crypto.subtle` and Node's `webcrypto` are assignable to it.
74
+ *
75
+ * @stability unstable
76
+ * @category models
77
+ * @since 4.1.1
78
+ */
79
+ export interface CryptoKey {
80
+ readonly algorithm: { readonly name: string }
81
+ readonly extractable: boolean
82
+ readonly type: "private" | "public" | "secret"
83
+ readonly usages: ReadonlyArray<string>
84
+ }
@@ -2,6 +2,7 @@ import * as Effect from "../../Effect.ts"
2
2
  import * as Predicate from "../../Predicate.ts"
3
3
  import * as CryptoError from "../CryptoError.ts"
4
4
  import type * as SecureRandom from "../SecureRandom.ts"
5
+ import type * as WebCrypto from "../WebCrypto.ts"
5
6
  import * as KeyModel from "./key.ts"
6
7
  import * as internal from "./webCrypto.ts"
7
8
 
@@ -121,7 +122,7 @@ export const fromCryptoKey = <K>(
121
122
  method: string,
122
123
  algorithm: unknown,
123
124
  role: Role,
124
- cryptoKey: CryptoKey
125
+ cryptoKey: WebCrypto.CryptoKey
125
126
  ): Effect.Effect<K, CryptoError.CryptoError> => {
126
127
  const spec = family.specOf(algorithm)
127
128
  return KeyModel.fromCryptoKey(
@@ -4,6 +4,7 @@ import { format } from "../../Formatter.ts"
4
4
  import { NodeInspectSymbol } from "../../Inspectable.ts"
5
5
  import * as Predicate from "../../Predicate.ts"
6
6
  import type * as CryptoError from "../CryptoError.ts"
7
+ import type * as WebCrypto from "../WebCrypto.ts"
7
8
  import * as internal from "./webCrypto.ts"
8
9
 
9
10
  /**
@@ -131,7 +132,7 @@ export const checkAesLength = (name: string, keyLength: number) => (algorithm: K
131
132
  return length === keyLength * 8 ? undefined : `Expected a ${keyLength * 8}-bit ${name} key, not ${length} bits`
132
133
  }
133
134
 
134
- const checkCryptoKey = (cryptoKey: CryptoKey, expected: ExpectedCryptoKey): string | undefined => {
135
+ const checkCryptoKey = (cryptoKey: WebCrypto.CryptoKey, expected: ExpectedCryptoKey): string | undefined => {
135
136
  if (
136
137
  !Predicate.hasProperty(cryptoKey, "type") || !Predicate.hasProperty(cryptoKey, "algorithm") ||
137
138
  !Predicate.hasProperty(cryptoKey, "usages") || !Array.isArray(cryptoKey.usages)
@@ -162,7 +163,7 @@ const checkCryptoKey = (cryptoKey: CryptoKey, expected: ExpectedCryptoKey): stri
162
163
  *
163
164
  * @internal
164
165
  */
165
- export const importedAlgorithm = (algorithm: unknown, cryptoKey: CryptoKey): unknown => {
166
+ export const importedAlgorithm = (algorithm: unknown, cryptoKey: WebCrypto.CryptoKey): unknown => {
166
167
  const modulusLength = (cryptoKey.algorithm as Partial<RsaKeyAlgorithm>).modulusLength
167
168
  return Predicate.hasProperty(algorithm, "modulusLength") && modulusLength !== undefined &&
168
169
  algorithm.modulusLength !== modulusLength
@@ -171,7 +172,8 @@ export const importedAlgorithm = (algorithm: unknown, cryptoKey: CryptoKey): unk
171
172
  }
172
173
 
173
174
  /**
174
- * Synchronously validates and wraps a `CryptoKey`.
175
+ * Synchronously validates and wraps a `CryptoKey`. The structural key only
176
+ * becomes the native `CryptoKey` once it passes the check.
175
177
  *
176
178
  * @internal
177
179
  */
@@ -180,13 +182,18 @@ export const fromCryptoKey = <K>(
180
182
  method: string,
181
183
  proto: object,
182
184
  algorithm: unknown,
183
- cryptoKey: CryptoKey,
185
+ cryptoKey: WebCrypto.CryptoKey,
184
186
  expected: ExpectedCryptoKey
185
187
  ): Effect.Effect<K, CryptoError.CryptoError> =>
186
188
  Effect.suspend(() => {
187
189
  const problem = checkCryptoKey(cryptoKey, expected)
188
190
  return problem === undefined
189
- ? Effect.succeed(make<K>(proto, importedAlgorithm(algorithm, cryptoKey), { _tag: "CryptoKey", cryptoKey }))
191
+ ? Effect.succeed(
192
+ make<K>(proto, importedAlgorithm(algorithm, cryptoKey), {
193
+ _tag: "CryptoKey",
194
+ cryptoKey: cryptoKey as CryptoKey
195
+ })
196
+ )
190
197
  : internal.invalidKey(module, method, problem)
191
198
  })
192
199
 
@@ -115,6 +115,7 @@ export const decode = (str: string): Result.Result<Uint8Array, EncodingError> =>
115
115
  })
116
116
  )
117
117
  }
118
+ // Validating up front keeps failures as cheap as the scan, without allocating the output.
118
119
  if (!/^[-_A-Z0-9]*?={0,2}$/i.test(stripped)) {
119
120
  return Result.fail(
120
121
  new EncodingError({
@@ -125,11 +126,34 @@ export const decode = (str: string): Result.Result<Uint8Array, EncodingError> =>
125
126
  })
126
127
  )
127
128
  }
128
- let sanitized = length % 4 === 2 ? `${stripped}==` : length % 4 === 3 ? `${stripped}=` : stripped
129
- sanitized = sanitized.replace(/-/g, "+").replace(/_/g, "/")
130
- return Base64.decode(sanitized)
129
+ const end = !padded ? length : stripped.charCodeAt(length - 2) === 61 ? length - 2 : length - 1
130
+ const fullEnd = end & ~3
131
+ // Not `(end * 3) >> 2`: engines that allow strings over 2^31 / 3 characters would overflow int32.
132
+ const result = new Uint8Array(Math.floor(end * 3 / 4))
133
+ let j = 0
134
+ for (let i = 0; i < fullEnd; i += 4) {
135
+ const a = codes[stripped.charCodeAt(i)]
136
+ const b = codes[stripped.charCodeAt(i + 1)]
137
+ const c = codes[stripped.charCodeAt(i + 2)]
138
+ const d = codes[stripped.charCodeAt(i + 3)]
139
+ result[j++] = (a << 2) | (b >> 4)
140
+ result[j++] = (b << 4) | (c >> 2)
141
+ result[j++] = (c << 6) | d
142
+ }
143
+ // Non-zero trailing bits in the final character are ignored.
144
+ if (end > fullEnd) {
145
+ const a = codes[stripped.charCodeAt(fullEnd)]
146
+ const b = codes[stripped.charCodeAt(fullEnd + 1)]
147
+ result[j++] = (a << 2) | (b >> 4)
148
+ if (end - fullEnd === 3) result[j] = (b << 4) | (codes[stripped.charCodeAt(fullEnd + 2)] >> 2)
149
+ }
150
+ return Result.succeed(result)
131
151
  }
132
152
 
153
+ const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
154
+ const codes = new Uint8Array(128)
155
+ for (let i = 0; i < alphabet.length; i++) codes[alphabet.charCodeAt(i)] = i
156
+
133
157
  /**
134
158
  * Decodes a padded or unpadded URL-safe Base64 string into UTF-8 text safely.
135
159
  *
@@ -236,7 +236,7 @@ const parseLine = (
236
236
  ignoreEmptyLines: boolean,
237
237
  unterminated: boolean
238
238
  ): NdjsonError | undefined => {
239
- if (ignoreEmptyLines && line.length === 0) return
239
+ if (ignoreEmptyLines && /^[ \t]*$/.test(line)) return
240
240
  try {
241
241
  out.push(JSON.parse(line))
242
242
  } catch (cause) {
@@ -262,8 +262,9 @@ const flushNothing = () => undefined
262
262
  *
263
263
  * **Gotchas**
264
264
  *
265
- * Set `ignoreEmptyLines` to skip blank lines before calling `JSON.parse`;
266
- * otherwise blank lines are parsed and fail as invalid JSON.
265
+ * Set `ignoreEmptyLines` to skip empty lines and lines containing only spaces
266
+ * or tabs before calling `JSON.parse`; otherwise these lines fail as invalid
267
+ * JSON.
267
268
  *
268
269
  * @stability unstable
269
270
  * @category constructors
@@ -314,8 +315,8 @@ const emptyBytes = new Uint8Array(0)
314
315
  * **Details**
315
316
  *
316
317
  * Lines may span input chunks. `\n`, `\r\n` and a bare `\r` end a line, and a
317
- * leading UTF-8 BOM is skipped. `ignoreEmptyLines` controls whether blank lines
318
- * are skipped before JSON parsing.
318
+ * leading UTF-8 BOM is skipped. `ignoreEmptyLines` skips empty lines and lines
319
+ * containing only spaces or tabs before JSON parsing.
319
320
  *
320
321
  * The first bad line fails the channel after the lines before it have been
321
322
  * emitted. A line that is not valid UTF-8 fails with `reason: "InvalidUtf8"`
@@ -667,7 +667,9 @@ export type AnyEvent = Event | Retry
667
667
  * **Details**
668
668
  *
669
669
  * It renders `Event` values as `id`, `event`, and `data` lines and renders
670
- * `Retry` values as `retry:` directives.
670
+ * `Retry` values as `retry:` directives. CR and LF are removed from `id` and
671
+ * `event`, and NUL is removed from `id`, so those values cannot inject fields
672
+ * or events.
671
673
  *
672
674
  * @stability unstable
673
675
  * @category encoding
@@ -679,10 +681,10 @@ export const encoder: Encoder = {
679
681
  case "Event": {
680
682
  let data = ""
681
683
  if (event.id !== undefined) {
682
- data += `id: ${event.id}\n`
684
+ data += `id: ${event.id.replace(/[\r\n\0]/g, "")}\n`
683
685
  }
684
686
  if (event.event !== "message") {
685
- data += `event: ${event.event}\n`
687
+ data += `event: ${event.event.replace(/[\r\n]/g, "")}\n`
686
688
  }
687
689
  data += `data: ${event.data.replace(/\r\n|\r|\n/g, "\ndata: ")}\n`
688
690
  return data + "\n"
@@ -303,6 +303,13 @@ export const layerRegistry = Layer.effect(
303
303
  * The identity is used by remote replication for authentication and by the
304
304
  * encryption service to derive signing and encryption keys.
305
305
  *
306
+ * **Gotchas**
307
+ *
308
+ * `publicKey` identifies the identity but is not a secret, and nothing ties it
309
+ * to the signing key derived from `privateKey`. A server binds the first
310
+ * signing key that authenticates for a `publicKey`, unless its authorization
311
+ * checks the presented signing key.
312
+ *
306
313
  * @stability unstable
307
314
  * @category services
308
315
  * @since 4.0.0
@@ -13,6 +13,7 @@ import type { NonEmptyReadonlyArray } from "../Array.ts"
13
13
  import * as Arr from "../Array.ts"
14
14
  import * as Cache from "../Cache.ts"
15
15
  import * as Context from "../Context.ts"
16
+ import * as Mac from "../crypto/Mac.ts"
16
17
  import * as Data from "../Data.ts"
17
18
  import * as Duration from "../Duration.ts"
18
19
  import * as Effect from "../Effect.ts"
@@ -76,6 +77,12 @@ export const layerAuthMiddleware: Layer.Layer<
76
77
  * chunked writes, delegates write and change handling to the supplied callbacks,
77
78
  * and frames large change payloads into chunks.
78
79
  *
80
+ * `Authenticate` calls `authorizeIdentity` with the claimed public key and the
81
+ * presented signing public key, then verifies the signature against the
82
+ * presented key. Only a verified key is passed to
83
+ * `getOrCreateSessionAuthBinding`, and a key that differs from the identity's
84
+ * existing binding is rejected.
85
+ *
79
86
  * Use the connection's `ConnectionWrites.track` in `onWrite` and
80
87
  * `ConnectionWrites.filter` in `changes` to suppress its own write echoes.
81
88
  * `compaction` sets `HelloResponse.compaction`, advertising that the server
@@ -89,7 +96,10 @@ export const layerRpcHandlers = (options: {
89
96
  readonly remoteId: RemoteId
90
97
  readonly firstSequence?: number | undefined
91
98
  readonly compaction?: boolean | undefined
92
- readonly authorizeIdentity?: (publicKey: string) => Effect.Effect<void, EventLogProtocolError>
99
+ readonly authorizeIdentity?: (
100
+ publicKey: string,
101
+ signingPublicKey: Uint8Array<ArrayBuffer>
102
+ ) => Effect.Effect<void, EventLogProtocolError>
93
103
  readonly getOrCreateSessionAuthBinding: (
94
104
  publicKey: string,
95
105
  signingPublicKey: Uint8Array<ArrayBuffer>
@@ -158,33 +168,40 @@ export const layerRpcHandlers = (options: {
158
168
  }
159
169
  yield* Cache.invalidate(clientChallenges, client.id)
160
170
  if (options.authorizeIdentity) {
161
- yield* options.authorizeIdentity(request.publicKey)
171
+ yield* options.authorizeIdentity(request.publicKey, request.signingPublicKey)
162
172
  }
163
- const signingPublicKey = yield* Cache.get(
164
- persistedSigningPublicKeys,
165
- new SessionAuthCacheKey({
166
- publicKey: request.publicKey,
167
- signingPublicKey: request.signingPublicKey
168
- })
169
- )
170
173
  const verified = yield* EventLogSessionAuth.verifySessionAuthenticateRequest({
171
174
  remoteId: options.remoteId,
172
175
  challenge,
173
176
  publicKey: request.publicKey,
174
- signingPublicKey,
177
+ signingPublicKey: request.signingPublicKey,
175
178
  signature: request.signature,
176
179
  algorithm: request.algorithm
177
180
  }).pipe(
178
181
  Effect.catch(() => Effect.succeed(false))
179
182
  )
180
183
 
181
- if (!verified) {
182
- return yield* new EventLogProtocolError({
184
+ const verificationFailed = () =>
185
+ new EventLogProtocolError({
183
186
  requestTag: "Authenticate",
184
187
  publicKey: request.publicKey,
185
188
  code: "Forbidden",
186
189
  message: "Session auth signature verification failed"
187
190
  })
191
+ if (!verified) {
192
+ return yield* verificationFailed()
193
+ }
194
+ // Only a verified key reaches the binding. A key other than the
195
+ // identity's bound key is rejected like a bad signature.
196
+ const boundSigningPublicKey = yield* Cache.get(
197
+ persistedSigningPublicKeys,
198
+ new SessionAuthCacheKey({
199
+ publicKey: request.publicKey,
200
+ signingPublicKey: request.signingPublicKey
201
+ })
202
+ )
203
+ if (!Mac.timingSafeEqual(boundSigningPublicKey, request.signingPublicKey)) {
204
+ return yield* verificationFailed()
188
205
  }
189
206
 
190
207
  const authenticatedIdentities = new Set(
@@ -411,6 +428,8 @@ export class AuthenticatedIdentities extends Context.Service<AuthenticatedIdenti
411
428
  "effect/eventlog/EventLogServer/AuthenticatedIdentities"
412
429
  ) {}
413
430
 
431
+ // Keyed by `publicKey` alone: the cache holds each identity's bound signing
432
+ // key, and `Authenticate` rejects any presented key that differs from it.
414
433
  class SessionAuthCacheKey extends Data.Class<{
415
434
  readonly publicKey: string
416
435
  readonly signingPublicKey: Uint8Array<ArrayBuffer>
@@ -503,6 +503,11 @@ export class Storage extends Context.Service<Storage, {
503
503
  * Lowest assigned sequence (default 0). Overestimating skips entries.
504
504
  */
505
505
  readonly firstSequence?: number | undefined
506
+ /**
507
+ * Returns the signing key bound to `publicKey`, binding `signingPublicKey`
508
+ * when none is bound yet. The server only passes keys whose signature it
509
+ * has verified, and rejects a key that differs from the returned binding.
510
+ */
506
511
  readonly getOrCreateSessionAuthBinding: (
507
512
  publicKey: string,
508
513
  signingPublicKey: Uint8Array<ArrayBuffer>
@@ -165,8 +165,8 @@ export const layerRpcHandlers: Layer.Layer<
165
165
  return EventLogServer.layerRpcHandlers({
166
166
  remoteId,
167
167
  firstSequence: storage.firstSequence,
168
- authorizeIdentity: (publicKey) =>
169
- auth.authorizeIdentity({ publicKey }).pipe(
168
+ authorizeIdentity: (publicKey, signingPublicKey) =>
169
+ auth.authorizeIdentity({ publicKey, signingPublicKey }).pipe(
170
170
  Effect.mapError((error) =>
171
171
  new EventLogProtocolError({
172
172
  requestTag: "Authenticate",
@@ -318,6 +318,17 @@ export class EventLogServerAuthError extends Data.TaggedError("EventLogServerAut
318
318
  * Use to provide authorization checks for plaintext event-log writes, reads,
319
319
  * and identity authentication.
320
320
  *
321
+ * **Gotchas**
322
+ *
323
+ * An identity's `publicKey` is an identifier, not a secret. The server binds
324
+ * the first signing key that authenticates for an identity and rejects any
325
+ * other key afterwards. `authorizeIdentity` also receives the presented
326
+ * `signingPublicKey`, so a deployment that records each identity's signing key
327
+ * at provisioning time, with `EventLogSessionAuth.deriveSigningPublicKey`, can
328
+ * reject every other key, including on first use. The server always supplies
329
+ * `signingPublicKey`; it is optional only so that existing callers keep
330
+ * compiling, and a policy that checks it should reject when it is missing.
331
+ *
321
332
  * @stability unstable
322
333
  * @category services
323
334
  * @since 4.0.0
@@ -334,6 +345,7 @@ export class EventLogServerAuthorization extends Context.Service<EventLogServerA
334
345
  }) => Effect.Effect<void, EventLogServerAuthError>
335
346
  readonly authorizeIdentity: (options: {
336
347
  readonly publicKey: string
348
+ readonly signingPublicKey?: Uint8Array<ArrayBuffer> | undefined
337
349
  }) => Effect.Effect<void, EventLogServerAuthError>
338
350
  }>()("effect/eventlog/EventLogServerUnencrypted/EventLogServerAuthorization") {}
339
351
 
@@ -422,6 +434,11 @@ export class Storage extends Context.Service<Storage, {
422
434
  * Lowest assigned sequence (default 0). Overestimating skips entries.
423
435
  */
424
436
  readonly firstSequence?: number | undefined
437
+ /**
438
+ * Returns the signing key bound to `publicKey`, binding `signingPublicKey`
439
+ * when none is bound yet. The server only passes keys whose signature it
440
+ * has verified, and rejects a key that differs from the returned binding.
441
+ */
425
442
  readonly getOrCreateSessionAuthBinding: (
426
443
  publicKey: string,
427
444
  signingPublicKey: Uint8Array<ArrayBuffer>
@@ -16,6 +16,8 @@ import { WebCrypto } from "../crypto/WebCrypto.ts"
16
16
  import * as Data from "../Data.ts"
17
17
  import * as Effect from "../Effect.ts"
18
18
  import * as Layer from "../Layer.ts"
19
+ import type { Identity } from "./EventLog.ts"
20
+ import { makeGetIdentityRootSecretMaterial } from "./internal/identityRootSecretDerivation.ts"
19
21
 
20
22
  const textEncoder = new TextEncoder()
21
23
  const textDecoder = new TextDecoder("utf-8", { fatal: true })
@@ -524,3 +526,27 @@ export const verifySessionAuthenticateRequest = Effect.fnUntraced(function*(opti
524
526
  signature: options.signature
525
527
  })
526
528
  })
529
+
530
+ const getIdentityRootSecretMaterial = makeGetIdentityRootSecretMaterial()
531
+
532
+ /**
533
+ * Derives the Ed25519 signing public key an identity presents during session
534
+ * authentication.
535
+ *
536
+ * **When to use**
537
+ *
538
+ * Use when provisioning an identity to record its signing key, so that server
539
+ * authorization can reject any other key presented for the identity's
540
+ * `publicKey`.
541
+ *
542
+ * **Details**
543
+ *
544
+ * The key is derived from the identity's private key on the `WebCrypto`
545
+ * reference.
546
+ *
547
+ * @stability unstable
548
+ * @category signing
549
+ * @since 4.1.1
550
+ */
551
+ export const deriveSigningPublicKey = (identity: Identity["Service"]): Effect.Effect<Uint8Array<ArrayBuffer>> =>
552
+ Effect.map(getIdentityRootSecretMaterial(identity), (material) => material.signingPublicKey)
@@ -70,7 +70,9 @@ type ArgsWithOptions<Variables, Options> = {} extends Variables ? [variables: Va
70
70
  * `partial: false`, it returns the decoded result and fails with a
71
71
  * `ResponseError` on any `errors`. With `partial: true` it returns
72
72
  * `{ data, errors }`. `partial` must be a literal `true` or `false`, not
73
- * an arbitrary `boolean`.
73
+ * an arbitrary `boolean`. A non-2xx HTTP response never succeeds, even with
74
+ * `partial: true`: it fails with a `ResponseError` when it has `errors`, and
75
+ * with a `TransportError` otherwise.
74
76
  *
75
77
  * An operation without variables is called as `client.Viewer()` or, with
76
78
  * options, `client.Viewer(undefined, { headers })`.