@aglyn/tenant-data-admin 1.0.0-beta.143

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 (374) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +7 -0
  3. package/jest.integration.config.d.ts +39 -0
  4. package/jest.integration.setup.d.ts +17 -0
  5. package/package.json +51 -0
  6. package/src/index.d.ts +17 -0
  7. package/src/index.js +18 -0
  8. package/src/index.js.map +1 -0
  9. package/src/lib/render-cache.d.ts +184 -0
  10. package/src/lib/render-cache.js +198 -0
  11. package/src/lib/render-cache.js.map +1 -0
  12. package/src/lib/server/account-addresses.d.ts +160 -0
  13. package/src/lib/server/account-addresses.js +311 -0
  14. package/src/lib/server/account-addresses.js.map +1 -0
  15. package/src/lib/server/account-emails.d.ts +219 -0
  16. package/src/lib/server/account-emails.js +640 -0
  17. package/src/lib/server/account-emails.js.map +1 -0
  18. package/src/lib/server/admin-audit.d.ts +180 -0
  19. package/src/lib/server/admin-audit.js +233 -0
  20. package/src/lib/server/admin-audit.js.map +1 -0
  21. package/src/lib/server/analytics-retention.d.ts +63 -0
  22. package/src/lib/server/analytics-retention.js +66 -0
  23. package/src/lib/server/analytics-retention.js.map +1 -0
  24. package/src/lib/server/api-http.d.ts +159 -0
  25. package/src/lib/server/api-http.js +230 -0
  26. package/src/lib/server/api-http.js.map +1 -0
  27. package/src/lib/server/api-keys.d.ts +117 -0
  28. package/src/lib/server/api-keys.js +298 -0
  29. package/src/lib/server/api-keys.js.map +1 -0
  30. package/src/lib/server/auth-handoff.d.ts +165 -0
  31. package/src/lib/server/auth-handoff.js +482 -0
  32. package/src/lib/server/auth-handoff.js.map +1 -0
  33. package/src/lib/server/auth-pools.d.ts +246 -0
  34. package/src/lib/server/auth-pools.js +551 -0
  35. package/src/lib/server/auth-pools.js.map +1 -0
  36. package/src/lib/server/billing-addons.d.ts +171 -0
  37. package/src/lib/server/billing-addons.js +277 -0
  38. package/src/lib/server/billing-addons.js.map +1 -0
  39. package/src/lib/server/campaign-attribution-store.d.ts +55 -0
  40. package/src/lib/server/campaign-attribution-store.js +95 -0
  41. package/src/lib/server/campaign-attribution-store.js.map +1 -0
  42. package/src/lib/server/campaign-conversion-attribution.d.ts +205 -0
  43. package/src/lib/server/campaign-conversion-attribution.js +279 -0
  44. package/src/lib/server/campaign-conversion-attribution.js.map +1 -0
  45. package/src/lib/server/client-error-report.d.ts +301 -0
  46. package/src/lib/server/client-error-report.js +755 -0
  47. package/src/lib/server/client-error-report.js.map +1 -0
  48. package/src/lib/server/connect-account-status.d.ts +74 -0
  49. package/src/lib/server/connect-account-status.js +94 -0
  50. package/src/lib/server/connect-account-status.js.map +1 -0
  51. package/src/lib/server/connect-payout-failure.d.ts +103 -0
  52. package/src/lib/server/connect-payout-failure.js +127 -0
  53. package/src/lib/server/connect-payout-failure.js.map +1 -0
  54. package/src/lib/server/console-api-rate-limit.d.ts +81 -0
  55. package/src/lib/server/console-api-rate-limit.js +111 -0
  56. package/src/lib/server/console-api-rate-limit.js.map +1 -0
  57. package/src/lib/server/console-domains.d.ts +326 -0
  58. package/src/lib/server/console-domains.js +779 -0
  59. package/src/lib/server/console-domains.js.map +1 -0
  60. package/src/lib/server/consume-once.d.ts +91 -0
  61. package/src/lib/server/consume-once.js +96 -0
  62. package/src/lib/server/consume-once.js.map +1 -0
  63. package/src/lib/server/contact-company-link.d.ts +85 -0
  64. package/src/lib/server/contact-company-link.js +105 -0
  65. package/src/lib/server/contact-company-link.js.map +1 -0
  66. package/src/lib/server/contact-email-engagement.d.ts +32 -0
  67. package/src/lib/server/contact-email-engagement.js +145 -0
  68. package/src/lib/server/contact-email-engagement.js.map +1 -0
  69. package/src/lib/server/contact-email-index.d.ts +56 -0
  70. package/src/lib/server/contact-email-index.js +158 -0
  71. package/src/lib/server/contact-email-index.js.map +1 -0
  72. package/src/lib/server/contact-lifecycle-floor.d.ts +89 -0
  73. package/src/lib/server/contact-lifecycle-floor.js +88 -0
  74. package/src/lib/server/contact-lifecycle-floor.js.map +1 -0
  75. package/src/lib/server/contact-merge.d.ts +55 -0
  76. package/src/lib/server/contact-merge.js +279 -0
  77. package/src/lib/server/contact-merge.js.map +1 -0
  78. package/src/lib/server/contact-suppression.d.ts +153 -0
  79. package/src/lib/server/contact-suppression.js +259 -0
  80. package/src/lib/server/contact-suppression.js.map +1 -0
  81. package/src/lib/server/crm-booking-activity.d.ts +86 -0
  82. package/src/lib/server/crm-booking-activity.js +251 -0
  83. package/src/lib/server/crm-booking-activity.js.map +1 -0
  84. package/src/lib/server/crm-email-activity.d.ts +112 -0
  85. package/src/lib/server/crm-email-activity.js +143 -0
  86. package/src/lib/server/crm-email-activity.js.map +1 -0
  87. package/src/lib/server/crm-inbound-email.d.ts +145 -0
  88. package/src/lib/server/crm-inbound-email.js +287 -0
  89. package/src/lib/server/crm-inbound-email.js.map +1 -0
  90. package/src/lib/server/crm-next-activity.d.ts +74 -0
  91. package/src/lib/server/crm-next-activity.js +194 -0
  92. package/src/lib/server/crm-next-activity.js.map +1 -0
  93. package/src/lib/server/crm-records.d.ts +144 -0
  94. package/src/lib/server/crm-records.js +218 -0
  95. package/src/lib/server/crm-records.js.map +1 -0
  96. package/src/lib/server/csp-aggregate.d.ts +62 -0
  97. package/src/lib/server/csp-aggregate.js +276 -0
  98. package/src/lib/server/csp-aggregate.js.map +1 -0
  99. package/src/lib/server/data-storage-gate.d.ts +66 -0
  100. package/src/lib/server/data-storage-gate.js +48 -0
  101. package/src/lib/server/data-storage-gate.js.map +1 -0
  102. package/src/lib/server/dataset-live-pages.d.ts +179 -0
  103. package/src/lib/server/dataset-live-pages.js +421 -0
  104. package/src/lib/server/dataset-live-pages.js.map +1 -0
  105. package/src/lib/server/dns-probe.d.ts +53 -0
  106. package/src/lib/server/dns-probe.js +184 -0
  107. package/src/lib/server/dns-probe.js.map +1 -0
  108. package/src/lib/server/document-id.d.ts +29 -0
  109. package/src/lib/server/document-id.js +100 -0
  110. package/src/lib/server/document-id.js.map +1 -0
  111. package/src/lib/server/domain-provider-vercel.d.ts +33 -0
  112. package/src/lib/server/domain-provider-vercel.js +298 -0
  113. package/src/lib/server/domain-provider-vercel.js.map +1 -0
  114. package/src/lib/server/domain-provider-webhook.d.ts +73 -0
  115. package/src/lib/server/domain-provider-webhook.js +223 -0
  116. package/src/lib/server/domain-provider-webhook.js.map +1 -0
  117. package/src/lib/server/domain-provider-wildcard.d.ts +54 -0
  118. package/src/lib/server/domain-provider-wildcard.js +163 -0
  119. package/src/lib/server/domain-provider-wildcard.js.map +1 -0
  120. package/src/lib/server/domain-provider.d.ts +197 -0
  121. package/src/lib/server/domain-provider.js +221 -0
  122. package/src/lib/server/domain-provider.js.map +1 -0
  123. package/src/lib/server/duplicate-activity.d.ts +54 -0
  124. package/src/lib/server/duplicate-activity.js +67 -0
  125. package/src/lib/server/duplicate-activity.js.map +1 -0
  126. package/src/lib/server/duplicate-resource.d.ts +55 -0
  127. package/src/lib/server/duplicate-resource.js +474 -0
  128. package/src/lib/server/duplicate-resource.js.map +1 -0
  129. package/src/lib/server/dynamic-list-materialize.d.ts +153 -0
  130. package/src/lib/server/dynamic-list-materialize.js +581 -0
  131. package/src/lib/server/dynamic-list-materialize.js.map +1 -0
  132. package/src/lib/server/edit-access-authz.d.ts +56 -0
  133. package/src/lib/server/edit-access-authz.js +107 -0
  134. package/src/lib/server/edit-access-authz.js.map +1 -0
  135. package/src/lib/server/edit-access-token.d.ts +72 -0
  136. package/src/lib/server/edit-access-token.js +116 -0
  137. package/src/lib/server/edit-access-token.js.map +1 -0
  138. package/src/lib/server/edit-hint-token.d.ts +83 -0
  139. package/src/lib/server/edit-hint-token.js +119 -0
  140. package/src/lib/server/edit-hint-token.js.map +1 -0
  141. package/src/lib/server/email-delivery-log.d.ts +509 -0
  142. package/src/lib/server/email-delivery-log.js +1043 -0
  143. package/src/lib/server/email-delivery-log.js.map +1 -0
  144. package/src/lib/server/email-flow-gate.d.ts +80 -0
  145. package/src/lib/server/email-flow-gate.js +153 -0
  146. package/src/lib/server/email-flow-gate.js.map +1 -0
  147. package/src/lib/server/email-marketing-gate.d.ts +333 -0
  148. package/src/lib/server/email-marketing-gate.js +639 -0
  149. package/src/lib/server/email-marketing-gate.js.map +1 -0
  150. package/src/lib/server/email-metering.d.ts +293 -0
  151. package/src/lib/server/email-metering.js +359 -0
  152. package/src/lib/server/email-metering.js.map +1 -0
  153. package/src/lib/server/email-revenue-attribution.d.ts +185 -0
  154. package/src/lib/server/email-revenue-attribution.js +300 -0
  155. package/src/lib/server/email-revenue-attribution.js.map +1 -0
  156. package/src/lib/server/email-send-rate.d.ts +232 -0
  157. package/src/lib/server/email-send-rate.js +417 -0
  158. package/src/lib/server/email-send-rate.js.map +1 -0
  159. package/src/lib/server/email-sender-reputation.d.ts +317 -0
  160. package/src/lib/server/email-sender-reputation.js +464 -0
  161. package/src/lib/server/email-sender-reputation.js.map +1 -0
  162. package/src/lib/server/email-suppression.d.ts +490 -0
  163. package/src/lib/server/email-suppression.js +648 -0
  164. package/src/lib/server/email-suppression.js.map +1 -0
  165. package/src/lib/server/email-topic-confirmation.d.ts +150 -0
  166. package/src/lib/server/email-topic-confirmation.js +272 -0
  167. package/src/lib/server/email-topic-confirmation.js.map +1 -0
  168. package/src/lib/server/email-unsubscribe-link.d.ts +174 -0
  169. package/src/lib/server/email-unsubscribe-link.js +236 -0
  170. package/src/lib/server/email-unsubscribe-link.js.map +1 -0
  171. package/src/lib/server/erase-person.d.ts +116 -0
  172. package/src/lib/server/erase-person.js +272 -0
  173. package/src/lib/server/erase-person.js.map +1 -0
  174. package/src/lib/server/erase.d.ts +458 -0
  175. package/src/lib/server/erase.js +1506 -0
  176. package/src/lib/server/erase.js.map +1 -0
  177. package/src/lib/server/firebase-admin.d.ts +71 -0
  178. package/src/lib/server/firebase-admin.js +343 -0
  179. package/src/lib/server/firebase-admin.js.map +1 -0
  180. package/src/lib/server/form-dataset-binding-token.d.ts +35 -0
  181. package/src/lib/server/form-dataset-binding-token.js +107 -0
  182. package/src/lib/server/form-dataset-binding-token.js.map +1 -0
  183. package/src/lib/server/free-workspace-cap.d.ts +201 -0
  184. package/src/lib/server/free-workspace-cap.js +404 -0
  185. package/src/lib/server/free-workspace-cap.js.map +1 -0
  186. package/src/lib/server/ga4-measurement-protocol.d.ts +256 -0
  187. package/src/lib/server/ga4-measurement-protocol.js +558 -0
  188. package/src/lib/server/ga4-measurement-protocol.js.map +1 -0
  189. package/src/lib/server/host-email-tokens.d.ts +47 -0
  190. package/src/lib/server/host-email-tokens.js +72 -0
  191. package/src/lib/server/host-email-tokens.js.map +1 -0
  192. package/src/lib/server/host-memberships.d.ts +54 -0
  193. package/src/lib/server/host-memberships.js +194 -0
  194. package/src/lib/server/host-memberships.js.map +1 -0
  195. package/src/lib/server/host-sending-domain.d.ts +292 -0
  196. package/src/lib/server/host-sending-domain.js +451 -0
  197. package/src/lib/server/host-sending-domain.js.map +1 -0
  198. package/src/lib/server/host-visitor-records.d.ts +150 -0
  199. package/src/lib/server/host-visitor-records.js +262 -0
  200. package/src/lib/server/host-visitor-records.js.map +1 -0
  201. package/src/lib/server/id-token-refusal.d.ts +28 -0
  202. package/src/lib/server/id-token-refusal.js +111 -0
  203. package/src/lib/server/id-token-refusal.js.map +1 -0
  204. package/src/lib/server/legal-acceptance.d.ts +214 -0
  205. package/src/lib/server/legal-acceptance.js +305 -0
  206. package/src/lib/server/legal-acceptance.js.map +1 -0
  207. package/src/lib/server/list-members.d.ts +211 -0
  208. package/src/lib/server/list-members.js +176 -0
  209. package/src/lib/server/list-members.js.map +1 -0
  210. package/src/lib/server/live-page-usage.d.ts +202 -0
  211. package/src/lib/server/live-page-usage.js +300 -0
  212. package/src/lib/server/live-page-usage.js.map +1 -0
  213. package/src/lib/server/lockdown.d.ts +313 -0
  214. package/src/lib/server/lockdown.js +643 -0
  215. package/src/lib/server/lockdown.js.map +1 -0
  216. package/src/lib/server/media-cdn-rate-limit.d.ts +188 -0
  217. package/src/lib/server/media-cdn-rate-limit.js +204 -0
  218. package/src/lib/server/media-cdn-rate-limit.js.map +1 -0
  219. package/src/lib/server/media-delivery.d.ts +267 -0
  220. package/src/lib/server/media-delivery.js +485 -0
  221. package/src/lib/server/media-delivery.js.map +1 -0
  222. package/src/lib/server/media-download-tokens.d.ts +161 -0
  223. package/src/lib/server/media-download-tokens.js +386 -0
  224. package/src/lib/server/media-download-tokens.js.map +1 -0
  225. package/src/lib/server/media-quarantine.d.ts +127 -0
  226. package/src/lib/server/media-quarantine.js +170 -0
  227. package/src/lib/server/media-quarantine.js.map +1 -0
  228. package/src/lib/server/media-signing.d.ts +126 -0
  229. package/src/lib/server/media-signing.js +159 -0
  230. package/src/lib/server/media-signing.js.map +1 -0
  231. package/src/lib/server/media-storage-path.d.ts +96 -0
  232. package/src/lib/server/media-storage-path.js +111 -0
  233. package/src/lib/server/media-storage-path.js.map +1 -0
  234. package/src/lib/server/media-strong-digest.d.ts +85 -0
  235. package/src/lib/server/media-strong-digest.js +190 -0
  236. package/src/lib/server/media-strong-digest.js.map +1 -0
  237. package/src/lib/server/media-tombstone.d.ts +276 -0
  238. package/src/lib/server/media-tombstone.js +421 -0
  239. package/src/lib/server/media-tombstone.js.map +1 -0
  240. package/src/lib/server/media-variants.d.ts +309 -0
  241. package/src/lib/server/media-variants.js +428 -0
  242. package/src/lib/server/media-variants.js.map +1 -0
  243. package/src/lib/server/member-email-aliases.d.ts +180 -0
  244. package/src/lib/server/member-email-aliases.js +420 -0
  245. package/src/lib/server/member-email-aliases.js.map +1 -0
  246. package/src/lib/server/member-photo.d.ts +88 -0
  247. package/src/lib/server/member-photo.js +185 -0
  248. package/src/lib/server/member-photo.js.map +1 -0
  249. package/src/lib/server/membership-recover-throttle.d.ts +211 -0
  250. package/src/lib/server/membership-recover-throttle.js +231 -0
  251. package/src/lib/server/membership-recover-throttle.js.map +1 -0
  252. package/src/lib/server/notifications.d.ts +55 -0
  253. package/src/lib/server/notifications.js +134 -0
  254. package/src/lib/server/notifications.js.map +1 -0
  255. package/src/lib/server/org-billing.d.ts +97 -0
  256. package/src/lib/server/org-billing.js +236 -0
  257. package/src/lib/server/org-billing.js.map +1 -0
  258. package/src/lib/server/organizations.d.ts +837 -0
  259. package/src/lib/server/organizations.js +1814 -0
  260. package/src/lib/server/organizations.js.map +1 -0
  261. package/src/lib/server/password-reset-throttle.d.ts +84 -0
  262. package/src/lib/server/password-reset-throttle.js +102 -0
  263. package/src/lib/server/password-reset-throttle.js.map +1 -0
  264. package/src/lib/server/personal-data-export.d.ts +241 -0
  265. package/src/lib/server/personal-data-export.js +858 -0
  266. package/src/lib/server/personal-data-export.js.map +1 -0
  267. package/src/lib/server/platform-domain-names.d.ts +62 -0
  268. package/src/lib/server/platform-domain-names.js +197 -0
  269. package/src/lib/server/platform-domain-names.js.map +1 -0
  270. package/src/lib/server/platform-marketing-consent.d.ts +133 -0
  271. package/src/lib/server/platform-marketing-consent.js +209 -0
  272. package/src/lib/server/platform-marketing-consent.js.map +1 -0
  273. package/src/lib/server/rate-limit-store.d.ts +702 -0
  274. package/src/lib/server/rate-limit-store.js +1220 -0
  275. package/src/lib/server/rate-limit-store.js.map +1 -0
  276. package/src/lib/server/realm-plugins.d.ts +67 -0
  277. package/src/lib/server/realm-plugins.js +250 -0
  278. package/src/lib/server/realm-plugins.js.map +1 -0
  279. package/src/lib/server/recaptcha-allowlist.d.ts +90 -0
  280. package/src/lib/server/recaptcha-allowlist.js +409 -0
  281. package/src/lib/server/recaptcha-allowlist.js.map +1 -0
  282. package/src/lib/server/release-flags.d.ts +105 -0
  283. package/src/lib/server/release-flags.js +183 -0
  284. package/src/lib/server/release-flags.js.map +1 -0
  285. package/src/lib/server/replay-erasures.d.ts +126 -0
  286. package/src/lib/server/replay-erasures.js +217 -0
  287. package/src/lib/server/replay-erasures.js.map +1 -0
  288. package/src/lib/server/resolve-people.d.ts +66 -0
  289. package/src/lib/server/resolve-people.js +112 -0
  290. package/src/lib/server/resolve-people.js.map +1 -0
  291. package/src/lib/server/safe-equal.d.ts +45 -0
  292. package/src/lib/server/safe-equal.js +58 -0
  293. package/src/lib/server/safe-equal.js.map +1 -0
  294. package/src/lib/server/sending-domain-debt.d.ts +127 -0
  295. package/src/lib/server/sending-domain-debt.js +268 -0
  296. package/src/lib/server/sending-domain-debt.js.map +1 -0
  297. package/src/lib/server/sending-domain-recheck.d.ts +151 -0
  298. package/src/lib/server/sending-domain-recheck.js +253 -0
  299. package/src/lib/server/sending-domain-recheck.js.map +1 -0
  300. package/src/lib/server/sending-domains.d.ts +364 -0
  301. package/src/lib/server/sending-domains.js +683 -0
  302. package/src/lib/server/sending-domains.js.map +1 -0
  303. package/src/lib/server/serve-media-cdn.d.ts +433 -0
  304. package/src/lib/server/serve-media-cdn.js +1370 -0
  305. package/src/lib/server/serve-media-cdn.js.map +1 -0
  306. package/src/lib/server/serve-plugin-fetch.d.ts +61 -0
  307. package/src/lib/server/serve-plugin-fetch.js +294 -0
  308. package/src/lib/server/serve-plugin-fetch.js.map +1 -0
  309. package/src/lib/server/sms-keywords.d.ts +63 -0
  310. package/src/lib/server/sms-keywords.js +146 -0
  311. package/src/lib/server/sms-keywords.js.map +1 -0
  312. package/src/lib/server/sso-break-glass-owners.d.ts +91 -0
  313. package/src/lib/server/sso-break-glass-owners.js +227 -0
  314. package/src/lib/server/sso-break-glass-owners.js.map +1 -0
  315. package/src/lib/server/sso-domain-policy.d.ts +140 -0
  316. package/src/lib/server/sso-domain-policy.js +239 -0
  317. package/src/lib/server/sso-domain-policy.js.map +1 -0
  318. package/src/lib/server/sso-drift-logic.d.ts +106 -0
  319. package/src/lib/server/sso-drift-logic.js +95 -0
  320. package/src/lib/server/sso-drift-logic.js.map +1 -0
  321. package/src/lib/server/sso-enforcement.d.ts +204 -0
  322. package/src/lib/server/sso-enforcement.js +309 -0
  323. package/src/lib/server/sso-enforcement.js.map +1 -0
  324. package/src/lib/server/sso-provisioning.d.ts +320 -0
  325. package/src/lib/server/sso-provisioning.js +715 -0
  326. package/src/lib/server/sso-provisioning.js.map +1 -0
  327. package/src/lib/server/staff-alert-email.d.ts +41 -0
  328. package/src/lib/server/staff-alert-email.js +63 -0
  329. package/src/lib/server/staff-alert-email.js.map +1 -0
  330. package/src/lib/server/stripe-account-mode.d.ts +156 -0
  331. package/src/lib/server/stripe-account-mode.js +162 -0
  332. package/src/lib/server/stripe-account-mode.js.map +1 -0
  333. package/src/lib/server/tenant-write-lockdown.d.ts +131 -0
  334. package/src/lib/server/tenant-write-lockdown.js +176 -0
  335. package/src/lib/server/tenant-write-lockdown.js.map +1 -0
  336. package/src/lib/server/test-firestore.d.ts +45 -0
  337. package/src/lib/server/test-firestore.js +124 -0
  338. package/src/lib/server/test-firestore.js.map +1 -0
  339. package/src/lib/server/token-revocation.d.ts +140 -0
  340. package/src/lib/server/token-revocation.js +255 -0
  341. package/src/lib/server/token-revocation.js.map +1 -0
  342. package/src/lib/server/update-existing.d.ts +32 -0
  343. package/src/lib/server/update-existing.js +73 -0
  344. package/src/lib/server/update-existing.js.map +1 -0
  345. package/src/lib/server/upload-cors-reconcile.d.ts +118 -0
  346. package/src/lib/server/upload-cors-reconcile.js +234 -0
  347. package/src/lib/server/upload-cors-reconcile.js.map +1 -0
  348. package/src/lib/server/upsert-contact.d.ts +262 -0
  349. package/src/lib/server/upsert-contact.js +546 -0
  350. package/src/lib/server/upsert-contact.js.map +1 -0
  351. package/src/lib/server/usage-invoice.d.ts +203 -0
  352. package/src/lib/server/usage-invoice.js +397 -0
  353. package/src/lib/server/usage-invoice.js.map +1 -0
  354. package/src/lib/server/user-profiles.d.ts +151 -0
  355. package/src/lib/server/user-profiles.js +364 -0
  356. package/src/lib/server/user-profiles.js.map +1 -0
  357. package/src/lib/server/vercel-drain-signature.d.ts +30 -0
  358. package/src/lib/server/vercel-drain-signature.js +61 -0
  359. package/src/lib/server/vercel-drain-signature.js.map +1 -0
  360. package/src/lib/server/vercel-log-drain.d.ts +259 -0
  361. package/src/lib/server/vercel-log-drain.js +479 -0
  362. package/src/lib/server/vercel-log-drain.js.map +1 -0
  363. package/src/lib/server/verify-email-cooldown.d.ts +102 -0
  364. package/src/lib/server/verify-email-cooldown.js +114 -0
  365. package/src/lib/server/verify-email-cooldown.js.map +1 -0
  366. package/src/lib/server/visitor-write-rate-limit.d.ts +87 -0
  367. package/src/lib/server/visitor-write-rate-limit.js +117 -0
  368. package/src/lib/server/visitor-write-rate-limit.js.map +1 -0
  369. package/src/lib/server/workspace-domains.d.ts +156 -0
  370. package/src/lib/server/workspace-domains.js +271 -0
  371. package/src/lib/server/workspace-domains.js.map +1 -0
  372. package/src/tenancy.d.ts +115 -0
  373. package/src/tenancy.js +142 -0
  374. package/src/tenancy.js.map +1 -0
@@ -0,0 +1,702 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import { type RateLimitResult } from './api-http';
18
+ /**
19
+ * Durable, cross-instance rate limiting (AGL-794).
20
+ *
21
+ * Every limiter in the codebase was a per-instance `Map`, each carrying its
22
+ * own "best-effort, serverless instances are ephemeral" caveat. On Vercel that
23
+ * is close to no limit at all: the counter resets on every cold start and each
24
+ * concurrent instance keeps its own, so the effective cap is roughly
25
+ * `limit × instances` and an attacker can widen it just by going wider. That
26
+ * is tolerable for blunting accidental bursts; it is not a brute-force
27
+ * defense.
28
+ *
29
+ * This backs the same fixed-window shape with a Firestore counter, so the cap
30
+ * is global. `api-http.ts` deliberately stays pure — it has no imports and is
31
+ * unit-tested directly — so the storage-backed variant lives here instead of
32
+ * being bolted onto it.
33
+ *
34
+ * **Cost is the reason this isn't the default everywhere.** Each call is a
35
+ * transaction (one read + one write). That is the right trade for a password
36
+ * unlock attempt; it is the wrong trade for an analytics beacon, which can
37
+ * fire on every page view. Use `checkRateLimit` for volume, this for
38
+ * consequence.
39
+ */
40
+ /** Collection holding one document per (key, window). Server-writes only. */
41
+ export declare const RATE_LIMIT_COLLECTION = "rateLimits";
42
+ /**
43
+ * Document-id prefix for degradation markers (AGL-1679).
44
+ *
45
+ * `degraded: true` used to exist only in a `console.error`, which means a
46
+ * Firestore blip silently dropped every durable limiter — auth, password
47
+ * reset, and now the public REST API's per-key quota — back to a per-instance
48
+ * cap for as long as it lasted, and nobody found out. Fail-soft is only a
49
+ * defensible choice if someone can tell that it fired.
50
+ *
51
+ * This is deliberately not an alerting stack. It is the cheapest thing that
52
+ * makes a degraded window answerable after the fact: when an episode ends,
53
+ * the instance writes one summary document into the SAME collection, so it
54
+ * inherits the deny-all rule and the `expiresAt` TTL policy that already
55
+ * exist rather than needing a new collection, a rules deploy and a second TTL
56
+ * policy. Ids are minute-bucketed so concurrent instances converge on a
57
+ * handful of documents:
58
+ *
59
+ * ```
60
+ * rateLimits/degraded_1755100800000
61
+ * ```
62
+ *
63
+ * Written on RECOVERY, never during the outage — the store is unreachable
64
+ * exactly when the episode is happening, so a marker written then would be
65
+ * the one write guaranteed to fail.
66
+ */
67
+ export declare const DEGRADATION_DOC_PREFIX = "degraded_";
68
+ interface DegradationEpisode {
69
+ /** Calls that fell back to the in-process limiter. */
70
+ count: number;
71
+ firstAtMs: number;
72
+ lastAtMs: number;
73
+ /** Last failure's code/message, truncated. */
74
+ code: string;
75
+ /** Last minute we logged, so a sustained outage cannot flood the log. */
76
+ lastLoggedBucket: number;
77
+ }
78
+ /**
79
+ * The degradation episode in progress on this instance, or `null`. Exposed
80
+ * for tests and for any future health surface; callers must not treat it as a
81
+ * global view — it only ever describes the instance that answers.
82
+ */
83
+ export declare function currentRateLimitDegradation(): Readonly<DegradationEpisode> | null;
84
+ /** Test seam: forget any in-progress episode. */
85
+ export declare function resetRateLimitDegradationForTests(): void;
86
+ /**
87
+ * Document-id prefix for signup-refusal markers (AGL-1907).
88
+ *
89
+ * `/api/orgs/create` has been rate-limited since AGL-1534 (3/h per uid, 10/h
90
+ * per IP) and AGL-1536 watches org-creation VOLUME — but volume only counts
91
+ * the signups that SUCCEEDED. A scripted farm that trips the limiter is
92
+ * therefore invisible in exactly the moment it is being contained: the 429s
93
+ * are the attack's signature and nothing recorded them. Before Sep 1 that is
94
+ * the difference between "the limiter held" and "we have no idea whether it
95
+ * was ever tested".
96
+ *
97
+ * Written into the SAME `rateLimits` collection as the counters and the
98
+ * AGL-1679 degradation markers, for the same reason that one gave: it inherits
99
+ * the deny-all security rule and the `expiresAt` TTL policy that already
100
+ * exist, instead of needing a new collection, a rules deploy and a second TTL
101
+ * policy. Minute-bucketed so concurrent instances converge:
102
+ *
103
+ * ```
104
+ * rateLimits/signupRefused_1755100800000
105
+ * ```
106
+ *
107
+ * **The timestamp field is `refusedAtMs`, deliberately not `lastAtMs`.** The
108
+ * AGL-1693 rate-limiter health probe queries this collection with
109
+ * `where('lastAtMs', '>=', cutoff).orderBy('lastAtMs','desc').limit(N)`. A
110
+ * refusal marker carrying `lastAtMs` would be picked up by that range and,
111
+ * under a flood, could fill the limit and push the real degradation markers
112
+ * out of the result — silently blinding a sibling alarm. A distinct field name
113
+ * keeps the two queries disjoint at the index level rather than relying on the
114
+ * id-prefix filter that runs after the read.
115
+ */
116
+ export declare const SIGNUP_REFUSAL_DOC_PREFIX = "signupRefused_";
117
+ /**
118
+ * What refused. Bounded on purpose — this is a public health body.
119
+ *
120
+ * `uid` and `ip` are this limiter's two caps. `locked` and `unreadable` are
121
+ * written by the `beforeUserCreated` blocking function in `cloud/functions`,
122
+ * which refuses on the Identity Platform path before any Aglyn route runs
123
+ * and therefore never reaches `consumeRateLimit` (AGL-2583). It cannot import
124
+ * this module — it is a plain npm package outside the workspace — so it
125
+ * writes the same document shape by hand and
126
+ * `apps/console/specs/signup-refusal-marker-wiring.spec.ts` fails if the two
127
+ * ever disagree about the collection, the prefix or the fields.
128
+ */
129
+ export type SignupRefusalReason = 'uid' | 'ip' | 'locked' | 'unreadable';
130
+ /**
131
+ * Record one refused org-creation attempt.
132
+ *
133
+ * Fire-and-forget and best-effort, like `flushDegradation`: refusing the
134
+ * request is the control, and failing a 429 because its breadcrumb could not
135
+ * be written would be strictly worse than not having the breadcrumb. Callers
136
+ * must not await this on the response path.
137
+ *
138
+ * **On the write-per-refusal cost.** A refused request is free to the attacker
139
+ * and now costs one transaction — the usual amplification objection. It does
140
+ * not apply here in kind, only in degree: `/api/orgs/create` already spends
141
+ * TWO `consumeRateLimit` transactions on every hit including the refused ones
142
+ * (AGL-1534 counts over-limit attempts by design), so this is a third write on
143
+ * a path that was already three, not a new class of cost. If it ever shows up
144
+ * on the bill the fix is in-process coalescing like `episode` above, not
145
+ * dropping the signal.
146
+ *
147
+ * Nothing identifying is stored. The counts are per-reason only; the uid and
148
+ * IP that were refused stay in the (hashed) limiter keys, which this never
149
+ * reads.
150
+ */
151
+ export declare function recordSignupRefusal(reason: SignupRefusalReason, options?: {
152
+ now?: number;
153
+ firestore?: any;
154
+ }): void;
155
+ /**
156
+ * Document-id prefix for server-error markers (AGL-1921).
157
+ *
158
+ * ## Why a Firestore marker when the errors already go to Cloud Logging
159
+ *
160
+ * `reportServerError` forwards every `onRequestError` to a `server-errors` log
161
+ * in `aglyn-main`, which is the right sink for triage — Error Reporting groups
162
+ * them and a log-match policy can page on them. It is not a sink anything in
163
+ * this repo can READ. Measured 2026-08-24 against the production credential:
164
+ * `POST logging.googleapis.com/v2/entries:list` for that log answers
165
+ * **403 `Permission denied for all log views`**, because the firebase-admin
166
+ * service account can create log entries and cannot list them. So the only
167
+ * reader the log has is a GCP alert policy that does not exist yet, and
168
+ * creating one is the click, not a thing that ships with a commit.
169
+ *
170
+ * That is the AGL-2486 shape exactly — a detector written into a place nothing
171
+ * watches. This marker is the second copy of the count, in a store we can
172
+ * already read, so `/api/health/server-errors` can grade it and the readers
173
+ * that already exist (the 15-minute GitHub uptime probe, the external keyword
174
+ * monitors, `docs.aglyn.com/status`) become the listeners on day one.
175
+ *
176
+ * Written into the SAME `rateLimits` collection as the counters, the AGL-1679
177
+ * degradation markers and the AGL-1907 refusal markers, for the reason those
178
+ * two gave: it inherits the deny-all security rule and the `expiresAt` TTL
179
+ * policy that already exist, instead of needing a new collection, a rules
180
+ * deploy and a second TTL policy — i.e. instead of needing a console action
181
+ * nobody can take from code. Minute bucketed so concurrent instances
182
+ * converge:
183
+ *
184
+ * ```
185
+ * rateLimits/serverError_1755100800000
186
+ * ```
187
+ *
188
+ * **The timestamp field is `erroredAtMs`**, deliberately neither `lastAtMs`
189
+ * (AGL-1679's) nor `refusedAtMs` (AGL-1907's), for the reason spelled out on
190
+ * `SIGNUP_REFUSAL_DOC_PREFIX`: each health probe range-queries its own field,
191
+ * and a shared field would let one signal's flood fill another's read limit
192
+ * and silently blind it. Three signals, three disjoint indexes.
193
+ */
194
+ export declare const SERVER_ERROR_DOC_PREFIX = "serverError_";
195
+ /**
196
+ * Record one uncaught server-side error, for `/api/health/server-errors`.
197
+ *
198
+ * Called from `reportServerError`, which is called from each app's
199
+ * `onRequestError` hook — so this counts exactly what that hook can see: an
200
+ * uncaught throw in a render or a route handler. It does NOT see an error that
201
+ * kills the process first, a platform-level 5xx (function timeout, OOM,
202
+ * cold-start 502), or anything thrown in the edge runtime. Those need the
203
+ * Vercel log drain; `docs/UPTIME_AND_SLA.md` carries the list.
204
+ *
205
+ * Fire-and-forget and never throws. Nothing identifying is stored — a count
206
+ * and which deployment produced it. The message, the stack and the route
207
+ * pattern stay in the Logging entry, which is not public; this marker is read
208
+ * by an endpoint that is.
209
+ */
210
+ export declare function recordServerError(service: string, options?: {
211
+ now?: number;
212
+ firestore?: any;
213
+ }): void;
214
+ /**
215
+ * Errors this instance is holding but has not written yet. Exposed for tests
216
+ * and for anyone reasoning about the coalescing window; callers must not treat
217
+ * it as a global view — it only ever describes the instance that answers.
218
+ */
219
+ export declare function pendingServerErrors(): number;
220
+ /** Test seam: forget anything held. */
221
+ export declare function resetServerErrorsForTests(): void;
222
+ /**
223
+ * Document-id prefix for the beacon's last-landed marker (AGL-2713).
224
+ *
225
+ * ## Why this one is not bucketed, and not queried
226
+ *
227
+ * Its four siblings above count OCCURRENCES over a window, so each one wants
228
+ * a stream of minute-bucketed documents and a range query over its own
229
+ * timestamp field. This answers a single question — *when did a heartbeat
230
+ * last reach Cloud Logging from this deployment* — so it is one document per
231
+ * service, read by id:
232
+ *
233
+ * ```
234
+ * rateLimits/beaconHeartbeat_console-web
235
+ * rateLimits/beaconHeartbeat_tenant-web
236
+ * ```
237
+ *
238
+ * A point read needs no index at all, which matters more than the cost: an
239
+ * index this repository cannot deploy without a token is an index that is not
240
+ * there when the failure is. Same collection as the siblings, for the reason
241
+ * they each gave — it inherits the deny-all rule and the `expiresAt` TTL
242
+ * policy that already exist, instead of needing a rules deploy nobody can
243
+ * make from code.
244
+ *
245
+ * **The timestamp field is `heartbeatAtMs`**, deliberately none of `lastAtMs`
246
+ * (AGL-1679), `refusedAtMs` (AGL-1907), `erroredAtMs` (AGL-1921) or
247
+ * `servedAtMs` (AGL-2583). This one is not range-queried, so the isolation
248
+ * argument the others make is not what earns it a field of its own — but a
249
+ * document carrying a sibling's field would enter that sibling's window and be
250
+ * counted as an episode it is not.
251
+ *
252
+ * ⚠️ **Evidence of a LANDING, never of a failure.** Nothing writes here when
253
+ * a heartbeat misses. So the worst a corrupt, stale or unreadable marker can
254
+ * do is make `/api/health/error-beacon` red sooner, and a deployment whose
255
+ * credential has never once worked has no marker at all and reds on its first
256
+ * probe. That asymmetry is what lets the door tolerate a miss without becoming
257
+ * a door that cannot go red.
258
+ */
259
+ export declare const BEACON_HEARTBEAT_DOC_PREFIX = "beaconHeartbeat_";
260
+ /**
261
+ * Record that a heartbeat reached Cloud Logging (AGL-2713).
262
+ *
263
+ * Fire-and-forget and never throws, like `flushServerErrors`: this runs
264
+ * inside a monitoring probe, and a breadcrumb must never be the reason the
265
+ * probe it is attached to fails. A write that does not land simply means the
266
+ * next miss is graded without it, which is the safe direction.
267
+ *
268
+ * The caller is memoized per instance on a five-minute TTL, so the write rate
269
+ * is one per instance per five minutes however hard the public endpoint is
270
+ * hit — orders of magnitude under the per-document ceiling that produced
271
+ * AGL-2404's contention storm.
272
+ */
273
+ export declare function recordBeaconHeartbeat(service: string, options?: {
274
+ now?: number;
275
+ firestore?: any;
276
+ }): void;
277
+ /**
278
+ * When a heartbeat last landed for this deployment, or null (AGL-2713).
279
+ *
280
+ * Null covers three cases that must all grade the same way: no marker has
281
+ * ever been written, the document holds nothing usable, and the store could
282
+ * not be read inside its budget. Every one of them means *there is no proof a
283
+ * heartbeat landed recently*, and the caller forgives a miss only on proof.
284
+ * Folding them is therefore not a shortcut — distinguishing them could only
285
+ * ever be used to forgive something unproven.
286
+ *
287
+ * The budget is the reason this is not a bare `get()`. It runs on the failure
288
+ * path, where the credential is already misbehaving, and a Firestore client
289
+ * whose own token will not mint retries with backoff for far longer than a
290
+ * health endpoint may take to answer.
291
+ */
292
+ export declare function readBeaconHeartbeat(service: string, options?: {
293
+ firestore?: any;
294
+ budgetMs?: number;
295
+ }): Promise<number | null>;
296
+ /**
297
+ * Document-id prefix for signup-page serve markers (AGL-2583).
298
+ *
299
+ * ## Why traffic is worth storing at all
300
+ *
301
+ * `/api/health/signups` counts orgs created in the trailing hour and goes red
302
+ * when there are TOO MANY. Zero — the reading that means nobody on earth can
303
+ * sign up — is its healthiest possible score, and that is how AGL-2581 refused
304
+ * every account creation for three days with the signup monitor green
305
+ * throughout. The missing half is a DENOMINATOR: zero accounts is an outage
306
+ * only if people were trying, and a quiet night otherwise.
307
+ *
308
+ * These markers are that denominator. Every render of the signup page fetches
309
+ * `/api/lockdown-status?feature=signups` to decide whether to show the paused
310
+ * notice, so that route is the one server touch a real arrival always makes,
311
+ * and counting it needs no new endpoint, no client beacon and no analytics
312
+ * vendor in the alerting path.
313
+ *
314
+ * Written into the SAME `rateLimits` collection as the counters and the three
315
+ * sibling marker kinds, for the reason they each gave: it inherits the
316
+ * deny-all security rule and the `expiresAt` TTL policy that already exist,
317
+ * instead of needing a new collection, a rules deploy and a second TTL policy.
318
+ * Minute bucketed so concurrent instances converge:
319
+ *
320
+ * ```
321
+ * rateLimits/signupServed_1755100800000
322
+ * ```
323
+ *
324
+ * **The timestamp field is `servedAtMs`**, deliberately none of `lastAtMs`
325
+ * (AGL-1679), `refusedAtMs` (AGL-1907) or `erroredAtMs` (AGL-1921), for the
326
+ * reason spelled out on `SIGNUP_REFUSAL_DOC_PREFIX`: each health probe
327
+ * range-queries its own field, and a shared field would let one signal's flood
328
+ * fill another's read limit and silently blind it. Four signals, four disjoint
329
+ * indexes.
330
+ */
331
+ export declare const SIGNUP_SERVED_DOC_PREFIX = "signupServed_";
332
+ /**
333
+ * Record one serving of the signup page, for `/api/health/signup-volume`.
334
+ *
335
+ * Nothing identifying is stored — a count and a timestamp. No IP, no user
336
+ * agent, no referrer, no campaign: this marker is read by an endpoint that is
337
+ * public, and the drought verdict needs a number, not an audience.
338
+ *
339
+ * Never throws, and callers must not await it.
340
+ */
341
+ export declare function recordSignupServed(options?: {
342
+ now?: number;
343
+ firestore?: any;
344
+ }): void;
345
+ /**
346
+ * Serves this instance is holding but has not written yet. Exposed for tests
347
+ * and for anyone reasoning about the coalescing window; it only ever describes
348
+ * the instance that answers.
349
+ */
350
+ export declare function pendingSignupServes(): number;
351
+ /** Test seam: forget anything held. */
352
+ export declare function resetSignupServesForTests(): void;
353
+ /**
354
+ * Document-id prefix for org-creation ATTEMPT markers (AGL-2714).
355
+ *
356
+ * ## Why serves were the wrong denominator
357
+ *
358
+ * The drought verdict beside this one asks "did traffic arrive and no account
359
+ * come out", and it took its denominator from `signupServed_` — a hit on
360
+ * `/api/lockdown-status?feature=signups`, which the signup page fetches on
361
+ * every render. That counts LOOKERS. Crawlers, link previews, a person
362
+ * reading the plan names and closing the tab, and the diagnostic page load
363
+ * somebody makes while investigating the alert itself all land in it.
364
+ *
365
+ * At this platform's conversion rate an hour with a handful of arrivals and
366
+ * no account is the ordinary case, so the check could not tell a door that
367
+ * does not open from a quiet afternoon — and it paged as though the service
368
+ * were down. No threshold fixes that; the quantity is wrong.
369
+ *
370
+ * An ATTEMPT is somebody who authenticated, filled the form and asked for an
371
+ * org. If those arrive and no org appears, something is broken, and the
372
+ * question has an answer that does not depend on how many people were merely
373
+ * browsing.
374
+ *
375
+ * ## Why this is stronger than the refusal marker beside it
376
+ *
377
+ * `signupRefusal_` counts requests this route TURNED AWAY, which is a
378
+ * deliberate outcome the code chose. An attempt marker is written before any
379
+ * outcome is known, so the pair spans what refusals cannot see on their own:
380
+ * a request that 500s, hangs, or dies in the platform between the limiter and
381
+ * the write leaves an attempt and no org, and no refusal at all.
382
+ *
383
+ * **The field is `attemptedAtMs`**, deliberately none of `lastAtMs`
384
+ * (AGL-1679), `refusedAtMs` (AGL-1907), `erroredAtMs` (AGL-1921) or
385
+ * `servedAtMs` (AGL-2583) — same rule the four before it follow: each health
386
+ * probe range-queries its own field, and a shared field would let one
387
+ * signal's flood fill another's read limit and silently blind it.
388
+ */
389
+ export declare const SIGNUP_ATTEMPT_DOC_PREFIX = "signupAttempted_";
390
+ /**
391
+ * Record one attempt to create an org, for `/api/health/signup-volume`.
392
+ *
393
+ * Written per attempt with no in-process coalescing, unlike the serve marker.
394
+ * The serve marker guards a PUBLIC page whose traffic is unbounded; this one
395
+ * sits behind an authenticated route the limiter already caps at 3 per uid and
396
+ * 10 per IP an hour, so the write rate is bounded by the limiter rather than
397
+ * by the internet. It is also the third write on a path that already spends
398
+ * two `consumeRateLimit` transactions per hit, which is the same accounting
399
+ * `recordSignupRefusal` sets out.
400
+ *
401
+ * `FieldValue.increment` with a merge rather than a transaction: increments
402
+ * are commutative server-side, so instances converging on one minute's
403
+ * document neither contend nor retry.
404
+ *
405
+ * Nothing identifying is stored — a count and a timestamp. This marker is read
406
+ * by an endpoint that is public, and the drought verdict needs a number, not
407
+ * an audience.
408
+ *
409
+ * Never throws, and callers must not await it: creating the org is the
410
+ * control, and a monitoring breadcrumb must never be the reason a signup gets
411
+ * slower or fails.
412
+ */
413
+ export declare function recordSignupAttempt(options?: {
414
+ now?: number;
415
+ firestore?: any;
416
+ }): void;
417
+ /**
418
+ * Wall-clock budget for one durable counter round trip (AGL-2404).
419
+ *
420
+ * Without a bound this function could not fail at all — it could only hang.
421
+ * The counter used to be a read-modify-write `runTransaction` on a single hot
422
+ * document: it contended, retried with backoff, and on a contended key
423
+ * routinely outran the platform's function ceiling, so the request died as a
424
+ * **504 with no body and no `Retry-After`** instead of the cheap 429 the
425
+ * limiter exists to produce. Measured against production on 2026-08-19 at
426
+ * `/api/protection/unlock`: sequential traffic refused cleanly at the 10th
427
+ * attempt, while **two** concurrent requests on one key were already enough
428
+ * to produce a 504 at ~10.3 s — the account-default function ceiling, since
429
+ * that route declares no `maxDuration`.
430
+ *
431
+ * AGL-2416 removed the read-modify-write (see {@link consumeRateLimit}), so
432
+ * the retry storm this bound was catching should no longer occur. The bound
433
+ * STAYS: it is what makes the function answerable at all, and a store that is
434
+ * slow for any other reason — a hot document at Firestore's own single-doc
435
+ * write ceiling, a network stall — must still produce a decision rather than
436
+ * a held function.
437
+ *
438
+ * 2.5 s is chosen to sit far below the smallest ceiling any caller runs at
439
+ * (10 s at the Vercel account default) while leaving room for the two round
440
+ * trips below, which measured ~0.6 s end to end on the same endpoint.
441
+ */
442
+ export declare const RATE_LIMIT_TRANSACTION_BUDGET_MS = 2500;
443
+ /**
444
+ * Thrown when the durable counter could not reach a decision inside
445
+ * {@link RATE_LIMIT_TRANSACTION_BUDGET_MS}. Distinct from a store *error* on
446
+ * purpose — see the classification note on {@link consumeRateLimit}.
447
+ */
448
+ export declare class RateLimitContentionError extends Error {
449
+ constructor(message?: string);
450
+ }
451
+ export interface DurableRateLimitOptions {
452
+ limit?: number;
453
+ windowMs?: number;
454
+ now?: number;
455
+ /** Injectable for tests; defaults to the Admin SDK's Firestore. */
456
+ firestore?: any;
457
+ /** Injectable for tests; defaults to {@link RATE_LIMIT_TRANSACTION_BUDGET_MS}. */
458
+ budgetMs?: number;
459
+ }
460
+ export interface DurableRateLimitResult extends RateLimitResult {
461
+ /**
462
+ * True when the durable store was unreachable and the in-memory limiter
463
+ * answered instead — the cap held for this instance only.
464
+ */
465
+ degraded: boolean;
466
+ /**
467
+ * True when the counter could not be read and written inside its budget
468
+ * because the key is contended, and the request was refused on that basis
469
+ * rather than on a counted overage (AGL-2404). Always `false` on an
470
+ * allowed result.
471
+ */
472
+ contended: boolean;
473
+ }
474
+ /**
475
+ * Counts one request against `key`'s fixed window, globally.
476
+ *
477
+ * Fails *soft, not open*: if Firestore is unreachable the in-memory limiter
478
+ * answers and the result is flagged `degraded`. Failing fully open would let
479
+ * an attacker disable brute-force protection by inducing a storage error;
480
+ * failing fully closed would lock legitimate visitors out of a site because of
481
+ * an unrelated Firestore blip. Degrading to the per-instance cap keeps some
482
+ * protection and keeps the site usable, and says which happened.
483
+ *
484
+ * ## Two failures, two postures (AGL-2404)
485
+ *
486
+ * "Firestore is unreachable" and "this one document is contended" are not the
487
+ * same event and must not get the same answer:
488
+ *
489
+ * - **The store is down** (fast error — `UNAVAILABLE`, a missing app, a
490
+ * credential failure). Fail SOFT, exactly as before: degrade to the
491
+ * per-instance cap and flag `degraded`. A real visitor must not lose access
492
+ * to a customer's site over an unrelated blip.
493
+ * - **The key is contended** (`ABORTED`, `DEADLINE_EXCEEDED`, or the budget
494
+ * above elapsing). Fail CLOSED: refuse with `contended: true` and let the
495
+ * caller answer 429 with its usual `Retry-After`. Degrading here would be a
496
+ * partial bypass — concurrent requests on ONE key are cheap to generate,
497
+ * and if they dropped the cap to a per-instance count then going wide would
498
+ * widen the cap, which is precisely the property AGL-794 removed.
499
+ *
500
+ * Refusing on contention is never worse than the 504 it replaces. A 504 is
501
+ * also a failed request, but it costs a full function timeout, carries no
502
+ * `Retry-After` for a well-behaved client to back off on, and invites an
503
+ * immediate retry that deepens the contention. Contention on a single key
504
+ * also means, at nearly every call site, either abuse or a client
505
+ * double-submitting: the keys are per (host, screen, IP), per uid, or per API
506
+ * key, so a legitimate visitor essentially never races themselves.
507
+ *
508
+ * ## The counter is an atomic increment, not a transaction (AGL-2416)
509
+ *
510
+ * It used to be `runTransaction(read count, write count + 1)`. That is
511
+ * optimistic concurrency on ONE document, so two writers in flight at once
512
+ * already race: measured in production on 2026-08-19, **two** concurrent
513
+ * requests on a single fresh key were enough to make a transaction lose,
514
+ * retry, and blow past its budget. Two is ordinary client behaviour — a
515
+ * double-submit, a mobile retry, two visitors behind one NAT — and the
516
+ * documented 120/min per API key is not even reachable without concurrency,
517
+ * so an integration built to the published budget met this by design.
518
+ *
519
+ * `set({count: increment(1)}, {merge: true})` is applied by the server with
520
+ * no read set to conflict over, so concurrent writers do not abort each
521
+ * other. The decision then needs the value, which costs a read back — the
522
+ * same two round trips a transaction spent, and the same billing: one
523
+ * document read plus one document write.
524
+ *
525
+ * **The trade is exactness for headroom, and it is deliberately biased.** The
526
+ * read-back can observe increments from writers that landed after ours, so
527
+ * under `C` concurrent requests a caller may see a count up to `C - 1` higher
528
+ * than its own position. The consequences, precisely:
529
+ *
530
+ * - It can only ever refuse EARLY, never late. The count is monotonic within
531
+ * a window and always includes this request's own increment, so an admitted
532
+ * request is one where at most `limit` increments had landed. The cap can
533
+ * never be exceeded, and going concurrent can never widen it.
534
+ * - "Exactly `limit` admitted" becomes "**at most** `limit` admitted". At the
535
+ * window's edge, `C` simultaneous requests can all read the same post-burst
536
+ * total and all be refused, so as few as `limit - C + 1` are admitted where
537
+ * `limit` would have been. Bounded by the concurrency, confined to one
538
+ * window, and in the customer-favourable direction for a *security* limiter
539
+ * — which is what all nine of this store's call sites are.
540
+ * - A fixed-window limiter already admits up to `2 × limit` across a window
541
+ * boundary, so exactness was never a property this shape had end to end.
542
+ * - **A call abandoned on its budget may already have counted.** The write
543
+ * lands before the read-back, so a caller that gives up during the read-back
544
+ * still spent its increment. The transaction it replaced committed nothing
545
+ * when it timed out — which is exactly how AGL-2416 could observe "10
546
+ * admitted" after a burst of 504s — so this is a genuine change, not a
547
+ * restatement. It errs in the same safe direction: the counter over-counts,
548
+ * so the cap still cannot be exceeded, and a caller can never buy budget by
549
+ * timing out.
550
+ *
551
+ * The failure classification above is unchanged and still load-bearing: the
552
+ * budget can still elapse on a genuinely slow store, and when it does the
553
+ * answer is still a refusal rather than a degradation.
554
+ */
555
+ export declare function consumeRateLimit(key: string, options?: DurableRateLimitOptions): Promise<DurableRateLimitResult>;
556
+ export default consumeRateLimit;
557
+ /**
558
+ * Document id for the signup canary's verdict (AGL-2715).
559
+ *
560
+ * One document, not a bucketed series: this records the LAST walk, and a
561
+ * history of walks answers a question nobody asks during an incident. The
562
+ * walk that matters is the most recent one.
563
+ *
564
+ * Written by the scheduled canary, read by `/api/health/journeys`. The split
565
+ * is the same one the beacon heartbeat makes and for a stronger reason: the
566
+ * canary CREATES AN ACCOUNT AND AN ORG, and the door that reports it is
567
+ * public and unauthenticated. A probe that walked the signup itself would
568
+ * hand anyone with `curl` an org factory.
569
+ *
570
+ * ⚠️ NOT in the `rateLimits` TTL's usual seven-day shape. There is exactly one
571
+ * of these and it is overwritten on every run, so it costs one document
572
+ * forever; `expiresAt` is still stamped, far enough out that a canary which
573
+ * stopped running expires its own evidence rather than leaving a stale pass
574
+ * lying around to be misread. The health verdict's own staleness window is
575
+ * two hours, so expiry is a backstop, not the mechanism.
576
+ */
577
+ export declare const SIGNUP_CANARY_DOC_ID = "signupCanary_production";
578
+ export interface SignupCanaryVerdict {
579
+ /** Whether every step of the walk succeeded. */
580
+ ok: boolean;
581
+ /** Which step broke. Never an error message, never a uid or an email. */
582
+ failedStep?: string | null;
583
+ /** Wall clock for the whole walk. */
584
+ elapsedMs: number;
585
+ /** Whether the walk deleted everything it created. */
586
+ reapedCleanly: boolean;
587
+ }
588
+ /**
589
+ * Record the outcome of one signup walk.
590
+ *
591
+ * AWAITED by its caller, unlike every other recorder in this file. Those are
592
+ * breadcrumbs on a request path where the user's work is the control and the
593
+ * marker must never delay it. This one IS the work: a canary whose verdict
594
+ * did not land has not reported, and the door it feeds would rather red on a
595
+ * missing marker than believe a walk nobody wrote down.
596
+ *
597
+ * `set` without merge, deliberately: this document is a snapshot of the last
598
+ * walk, and merging would let a field from a previous run survive into a
599
+ * verdict it was not part of — a `failedStep` outliving the failure that
600
+ * produced it, read beside a fresh `ok: true`.
601
+ */
602
+ export declare function recordSignupCanaryWalk(verdict: SignupCanaryVerdict, options?: {
603
+ now?: number;
604
+ firestore?: any;
605
+ }): Promise<void>;
606
+ /**
607
+ * The last recorded walk, or null (AGL-2715).
608
+ *
609
+ * Null covers the same three cases `readBeaconHeartbeat` folds, for the same
610
+ * reason: no marker has ever been written, the document holds nothing usable,
611
+ * and the store would not answer inside its budget. Each one means *nothing
612
+ * here has demonstrated that a stranger can sign up*, and the verdict grades
613
+ * all three as `canary-unavailable`. Distinguishing them could only ever be
614
+ * used to forgive something unproven.
615
+ */
616
+ export declare function readSignupCanaryWalk(options?: {
617
+ firestore?: any;
618
+ budgetMs?: number;
619
+ }): Promise<{
620
+ walkedAtMs?: number;
621
+ ok?: boolean;
622
+ failedStep?: string | null;
623
+ elapsedMs?: number;
624
+ reapedCleanly?: boolean;
625
+ } | null>;
626
+ /**
627
+ * Document id for the App Check attestation sample (AGL-2715).
628
+ *
629
+ * The rate is published by Google to Cloud Monitoring, which a public health
630
+ * endpoint has no business calling: it needs a second credential, it is slow,
631
+ * and its quota is not something a route anyone can curl should be able to
632
+ * spend. So a scheduled job samples it and this holds the answer, exactly as
633
+ * the signup canary's own verdict is held.
634
+ *
635
+ * One document, overwritten each run — the last reading is the only one that
636
+ * answers "is attestation working now".
637
+ */
638
+ /**
639
+ * Where the metered-traffic sampler leaves its reading (AGL-2720).
640
+ *
641
+ * One document, like every sibling here. The sampler owns the judgement about
642
+ * whether the total moved; this file only carries it.
643
+ */
644
+ export declare const EDGE_ADMISSION_DOC_ID = "edgeAdmission_production";
645
+ /**
646
+ * Read the last metered-traffic sample.
647
+ *
648
+ * Deliberately has NO max-age rejection, which is the one place this differs
649
+ * from {@link readAppCheckAttestation}. The quantity graded is already an age
650
+ * — how long since traffic moved — so discarding an old marker would turn the
651
+ * loudest possible reading into `edge-admission-unavailable` and lose which
652
+ * of the two things broke.
653
+ */
654
+ export declare function readEdgeAdmission(options?: {
655
+ firestore?: any;
656
+ budgetMs?: number;
657
+ }): Promise<{
658
+ sampledAtMs?: number;
659
+ advancedAtMs?: number;
660
+ day?: string;
661
+ total?: number;
662
+ } | null>;
663
+ export declare const APP_CHECK_ATTESTATION_DOC_ID = "appCheckAttestation_production";
664
+ /**
665
+ * Record one sampling of the App Check verification counts.
666
+ *
667
+ * Awaited by its caller, like the canary's verdict and unlike the breadcrumbs
668
+ * in this file: the sample IS the work, and a door would rather report a
669
+ * missing reading than believe one nobody wrote down.
670
+ */
671
+ export declare function recordAppCheckAttestation(samples: Array<{
672
+ result?: string;
673
+ security?: string;
674
+ service?: string;
675
+ count?: number;
676
+ }>, options?: {
677
+ now?: number;
678
+ firestore?: any;
679
+ }): Promise<void>;
680
+ /**
681
+ * The last attestation sample, or null (AGL-2715).
682
+ *
683
+ * Null folds the three cases its siblings fold — never written, unusable, and
684
+ * the store would not answer — because each means *nothing here has shown that
685
+ * attestation is working*, and the verdict grades all three as
686
+ * `attestation-unavailable`.
687
+ *
688
+ * A sample older than the window it describes is also null. A stale reading is
689
+ * not a reading: reporting a six-hour-old rate as current is how a check comes
690
+ * to say attestation is fine long after it stopped being.
691
+ */
692
+ export declare function readAppCheckAttestation(options?: {
693
+ firestore?: any;
694
+ budgetMs?: number;
695
+ now?: number;
696
+ maxAgeMs?: number;
697
+ }): Promise<Array<{
698
+ result?: string;
699
+ security?: string;
700
+ service?: string;
701
+ count?: number;
702
+ }> | null>;