@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 @@
1
+ {"version":3,"sources":["../../../../../../../../libs/tenant/data/admin/src/lib/server/sso-enforcement.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * SSO enforcement sweep (AGL-1129): when an org turns on `sso.enforced`,\n * every account in its GCIP tenant loses any sign-in method that is not the\n * org's own IdP.\n *\n * AGL-1128 stopped SSO-governed accounts from linking NEW consumer providers.\n * This is the grandfathering half: an account that linked Google before\n * enforcement keeps a way in that the customer's IdP cannot see or revoke, and\n * a bypass that predates the switch is still a bypass.\n *\n * Runs against the TENANT pool via `authForPool` — project-level Admin SDK\n * calls cannot see, let alone mutate, these accounts (AGL-1122).\n *\n * Three properties this must have, in order of how badly they bite:\n *\n * 1. **Never unlink down to zero.** An account whose only provider is the one\n * we are about to remove would be orphaned — reachable by nobody, not even\n * the IdP. Skipped and reported, never assumed away: being in the tenant\n * does not prove the SAML link is present.\n * 2. **Revoke refresh tokens for accounts it actually changed.** Unlinking\n * does not end a live session, so without this the bypass simply has a\n * clock on it. Only for accounts that changed — a sweep that revoked\n * everyone would sign out the org's whole staff on a no-op run.\n * 3. **Idempotent.** Re-running is the natural way to catch accounts created\n * between the flag flip and the sweep, so a second run must be a no-op\n * rather than a second round of revocations.\n * 4. **Never leave the ORG with no way in** (AGL-1888). Property 1 protects\n * one account at a time and is blind to the org-level failure: strip every\n * account down to the SAML link and the pool is perfectly consistent right\n * up until the IdP stops answering, at which point nobody can sign in and\n * we cannot let them back in either. The `aglyn-org-y5v14` tenant is in\n * that state today. So enforcement REFUSES unless the org keeps a way in\n * that does\n * not depend on the IdP: either a DESIGNATED break-glass account inside\n * the pool, or an org owner who lives OUTSIDE it\n * ({@link findBreakGlassOrgOwners}). The second is what makes the first\n * satisfiable at all — nothing inside a pool we provisioned can hold a\n * password, so for a while this guard could only ever say no.\n */\n\nimport type { UserRecord } from 'firebase-admin/auth'\nimport { FieldValue } from 'firebase-admin/firestore'\nimport { authForPool } from './auth-pools'\nimport {\n findBreakGlassOrgOwners,\n type SsoBreakGlassOwner,\n} from './sso-break-glass-owners'\nimport { invalidateTokenRevocationCache } from './token-revocation'\nimport firebaseAdmin from './firebase-admin'\nimport { notifyUsers } from './notifications'\n\nconst firestore = () => firebaseAdmin.app().firestore()\n\n/** How many tenant accounts one sweep will walk. Enterprise pools are small. */\nconst TENANT_USER_CAP = 1000\n\nexport interface SsoEnforcementOptions {\n /** Staff uid recorded on the audit rows; `system` for an automated run. */\n actorUid?: string\n /** Report what would change without touching anything. */\n dryRun?: boolean\n /**\n * Run even when `sso.enforced` is false. ONLY for a rehearsal against a\n * throwaway account — the flag is the customer's instruction, and acting\n * without it removes sign-in methods nobody asked us to remove.\n */\n force?: boolean\n}\n\n/** Why an account with removable providers was left alone. */\nexport type SsoEnforcementSkip = 'would-orphan' | 'break-glass'\n\nexport interface SsoEnforcementAccount {\n uid: string\n email: string | null\n /** Providers removed (or that would be, in a dry run). */\n unlinked: string[]\n /** Providers left in place. */\n kept: string[]\n /** Why an account with removable providers was left alone. */\n skipped?: SsoEnforcementSkip\n}\n\n/**\n * Whether this org would still have a way in if its IdP stopped answering\n * (AGL-1888).\n */\nexport interface SsoLockoutAssessment {\n /**\n * True when the org keeps at least one way in that its IdP does not\n * mediate: a DESIGNATED break-glass account inside the pool, or an org\n * owner outside it.\n */\n safe: boolean\n /** Designated break-glass uids that actually retain a non-IdP credential. */\n retainedBy: string[]\n /**\n * Designated uids that do NOT protect anybody — either absent from the pool,\n * or holding nothing but the IdP link. Named rather than silently ignored:\n * a designation that protects nothing is worse than none, because it reads\n * as protection.\n */\n ineffective: string[]\n /**\n * Org owners in the PROJECT pool (AGL-1888 option (a)). The sweep cannot\n * see these accounts, so an IdP that stops answering cannot lock them out.\n * Named, not counted: the admin has to recognise the person.\n */\n ownersOutsidePool: SsoBreakGlassOwner[]\n /**\n * The owner lookup could not be completed. `ownersOutsidePool` is then\n * INCOMPLETE rather than empty, so the refusal must say we could not check\n * instead of asserting the org has nobody. Never makes an org safe.\n */\n ownerLookupFailed: boolean\n}\n\nexport interface SsoEnforcementResult {\n orgId: string\n tenantId: string\n providerId: string\n dryRun: boolean\n /** Accounts walked. */\n scanned: number\n /** Accounts whose providers changed (0 on a converged re-run). */\n changed: number\n accounts: SsoEnforcementAccount[]\n /** True when the tenant holds more accounts than one sweep walks. */\n truncated: boolean\n /**\n * Whether the org retains a way in that does not depend on its IdP\n * (AGL-1888). Present on the DRY RUN too — that is the point, since the\n * rehearsal is where an org is supposed to discover it needs one.\n */\n lockout: SsoLockoutAssessment\n}\n\nexport class SsoEnforcementError extends Error {}\n\n/**\n * Strip non-IdP sign-in methods from every account in an org's SSO tenant.\n *\n * Throws when the org has no active SSO config, or when `sso.enforced` is\n * false without `force` — enforcement is the customer's switch, and running\n * this without it would be us deciding to remove their people's sign-in\n * methods on our own.\n */\nexport async function enforceSsoSignInMethods(\n orgId: string,\n options: SsoEnforcementOptions = {},\n): Promise<SsoEnforcementResult> {\n const { actorUid = 'system', dryRun = false, force = false } = options\n const db = firestore()\n const orgSnapshot = await db.collection('orgs').doc(orgId).get()\n if (!orgSnapshot.exists) {\n throw new SsoEnforcementError(`No such organization: ${orgId}`)\n }\n const sso = orgSnapshot.get('sso') as\n | {\n tenantId?: string\n providerId?: string\n enforced?: boolean\n status?: string\n /** Accounts the org designated to keep a non-IdP way in (AGL-1888). */\n breakGlassUids?: unknown\n }\n | undefined\n const tenantId = sso?.tenantId\n const providerId = sso?.providerId\n if (!tenantId || !providerId) {\n throw new SsoEnforcementError(`${orgId} has no SSO tenant configured`)\n }\n if (sso?.status !== 'active') {\n throw new SsoEnforcementError(\n `${orgId} SSO status is \"${sso?.status ?? 'unset'}\", not active`,\n )\n }\n if (!sso?.enforced && !force) {\n throw new SsoEnforcementError(\n `${orgId} has sso.enforced = false — nothing to enforce`,\n )\n }\n\n const breakGlassUids = Array.isArray(sso?.breakGlassUids)\n ? (sso.breakGlassUids as unknown[]).map(String).filter(Boolean)\n : []\n\n const pool = authForPool(tenantId)\n const page = await pool.listUsers(TENANT_USER_CAP)\n const accounts: SsoEnforcementAccount[] = []\n const changedUids: string[] = []\n const breakGlass = new Set(breakGlassUids)\n\n for (const record of page.users) {\n const summary = planAccount(record, providerId, breakGlass)\n accounts.push(summary)\n }\n\n // The half the pool cannot supply (AGL-1888 option (a)). Run for the DRY\n // RUN too: the rehearsal is where an org learns whether it may enforce, and\n // a rehearsal that skipped this would report a lockout risk the real run\n // then disagrees with.\n const ownerLookup = await findBreakGlassOrgOwners(orgId, providerId, tenantId)\n const lockout = assessSsoLockoutRisk(\n accounts,\n providerId,\n breakGlassUids,\n ownerLookup.owners,\n ownerLookup.unavailable,\n )\n\n // THE PRE-FLIGHT (AGL-1888). Planned first, refused before a single\n // `updateUser` — this is the whole reason the plan is computed separately\n // from being carried out. Half a sweep is the worst outcome available: the\n // accounts already stripped are stripped, and the org is locked out with no\n // record of how far it got.\n //\n // The dry run is exempt, and has to be. The rehearsal is exactly how an org\n // FINDS OUT it needs a break-glass account, so refusing to rehearse would\n // leave them guessing at the requirement they are being held to.\n //\n // No override parameter. An `acknowledgeLockoutRisk: true` a caller can pass\n // is not a guard, it is a spelling of the guard being off — and the caller\n // that would pass it is the one already convinced enforcement is fine.\n if (!dryRun && !lockout.safe) {\n throw new SsoEnforcementError(SSO_LOCKOUT_REFUSAL)\n }\n\n for (const summary of accounts) {\n const record = page.users.find((user) => user.uid === summary.uid)\n if (!record) continue\n if (!summary.unlinked.length || summary.skipped) continue\n if (!dryRun) {\n await pool.updateUser(record.uid, {\n providersToUnlink: summary.unlinked,\n })\n // Only for accounts that actually changed (property 2 above). An\n // existing session outlives the unlink otherwise.\n await pool.revokeRefreshTokens(record.uid)\n // This process refuses the old token NOW (AGL-1881); every other one\n // converges within TOKEN_REVOCATION_TTL_MS.\n invalidateTokenRevocationCache(record.uid, tenantId)\n await db.collection('adminAudit').add({\n actorUid,\n action: 'org.sso.enforceSignInMethods',\n target: `users/${record.uid}`,\n before: { providers: providerIdsOf(record) },\n after: {\n providers: summary.kept,\n orgId,\n tenantId,\n unlinked: summary.unlinked,\n tokensRevoked: true,\n },\n at: FieldValue.serverTimestamp(),\n })\n }\n changedUids.push(record.uid)\n }\n\n if (!dryRun && changedUids.length) {\n // Explaining it beats a mysteriously missing button on their next visit.\n await notifyUsers(changedUids, {\n type: 'system.signInMethodRemoved',\n title: 'Your sign-in methods changed',\n body:\n 'Your organization now requires single sign-on, so other sign-in ' +\n 'methods have been removed from your account. Sign in through your ' +\n 'organization instead.',\n orgId,\n link: '/manage/user',\n })\n }\n\n return {\n orgId,\n tenantId,\n providerId,\n dryRun,\n scanned: page.users.length,\n changed: changedUids.length,\n accounts,\n truncated: Boolean(page.pageToken),\n lockout,\n }\n}\n\nconst providerIdsOf = (record: UserRecord): string[] =>\n (record.providerData ?? [])\n .map((info) => info?.providerId)\n .filter((id): id is string => Boolean(id))\n\n/**\n * What one account loses, decided without mutating anything so the dry run\n * and the real run cannot disagree.\n *\n * `password` counts as a provider to strip: an email/password credential on a\n * governed account is exactly the sort of standing bypass enforcement is\n * bought to remove, and it is not the org's IdP.\n *\n * @param breakGlass uids the org has DESIGNATED to keep their other sign-in\n * methods (AGL-1888). Deliberately a parameter rather than a lookup:\n * this function is the whole decision and stays pure, so the rehearsal\n * and the real run cannot diverge on it either.\n */\nexport function planAccount(\n record: UserRecord,\n providerId: string,\n breakGlass: ReadonlySet<string> = new Set(),\n): SsoEnforcementAccount {\n const providers = providerIdsOf(record)\n const unlinked = providers.filter((id) => id !== providerId)\n const kept = providers.filter((id) => id === providerId)\n const summary: SsoEnforcementAccount = {\n uid: record.uid,\n email: record.email ?? null,\n unlinked,\n kept,\n }\n // Property 1: an account with nothing but removable providers keeps them.\n // It is misconfigured, not a bypass to close, and orphaning it would turn a\n // security tightening into a lockout with no way back.\n if (unlinked.length && !kept.length) {\n return { ...summary, unlinked: [], kept: providers, skipped: 'would-orphan' }\n }\n // Property 4 (AGL-1888): a designated break-glass account keeps everything.\n //\n // This IS a standing bypass of SSO enforcement, and it is meant to be —\n // that is what makes it a way back in when the IdP stops answering. The\n // alternative is the state the `aglyn-org-y5v14` tenant is in today: every\n // account in the pool holds nothing but a SAML link, and a lapsed\n // certificate or a deleted SAML app locks the organization out of itself\n // permanently.\n //\n // Checked AFTER the orphan rule so the two cannot disagree about an account\n // that is both, and it changes nothing for a break-glass account that has\n // no removable provider anyway — `skipped` marks a decision, so it is set\n // only where there was something to decide.\n if (unlinked.length && breakGlass.has(record.uid)) {\n return { ...summary, unlinked: [], kept: providers, skipped: 'break-glass' }\n }\n return summary\n}\n\n/**\n * Would this org still have a way in after the sweep? (AGL-1888)\n *\n * Pure, and over the PLANNED accounts rather than the raw records, so it\n * answers the question about the state enforcement is actually about to\n * create rather than the one it is in now.\n *\n * ONLY A DESIGNATED ACCOUNT COUNTS. An account that happens to retain a\n * password because it is misconfigured — the `would-orphan` case, which has no\n * IdP link at all — is not break-glass: nobody chose it, it may be a stale\n * record or a service account, and an org protected by an accident it does not\n * know about is not protected. Requiring the designation is what makes this a\n * control rather than a coincidence.\n *\n * A designation only counts when the account actually holds something OTHER\n * than the org's IdP. Naming an account whose sole credential is the SAML link\n * is the most natural way to get this wrong — it looks like a break-glass\n * account and provides nothing, because it fails in exactly the situation it\n * exists for.\n *\n * THE SECOND WAY IN IS NOT IN THE POOL AT ALL (AGL-1888 option (a)). An org\n * owner holding a project-level credential is invisible to the sweep — the\n * enforcement pass lists `authForPool(tenantId)` and never touches the\n * project pool — so a lapsed certificate cannot reach them. No designation is\n * required for those: nobody has to be told to keep the key they already\n * hold, and requiring a tick would put the requirement back out of reach for\n * exactly the org that already satisfies it. Whether such an owner exists is\n * decided by {@link findBreakGlassOrgOwners}, which is where the seven\n * conditions live; this function only counts what it is handed, so that the\n * rehearsal and the real run cannot disagree.\n *\n * `ownerLookupFailed` deliberately does NOT make an org safe. It exists so a\n * refusal can distinguish \"you have nobody\" from \"we could not check\" — an\n * error swallowed into an empty list renders as a measured zero, and nothing\n * about it looks wrong.\n */\nexport function assessSsoLockoutRisk(\n accounts: readonly SsoEnforcementAccount[],\n providerId: string,\n breakGlass: readonly string[],\n ownersOutsidePool: readonly SsoBreakGlassOwner[] = [],\n ownerLookupFailed = false,\n): SsoLockoutAssessment {\n const designated = [...new Set(breakGlass.filter(Boolean))]\n const byUid = new Map(accounts.map((account) => [account.uid, account]))\n const retainedBy: string[] = []\n const ineffective: string[] = []\n for (const uid of designated) {\n const account = byUid.get(uid)\n const protects = Boolean(\n account?.kept.some((id) => id !== providerId),\n )\n if (protects) retainedBy.push(uid)\n else ineffective.push(uid)\n }\n const owners = [...ownersOutsidePool]\n return {\n safe: retainedBy.length > 0 || owners.length > 0,\n retainedBy,\n ineffective,\n ownersOutsidePool: owners,\n ownerLookupFailed,\n }\n}\n\n/**\n * The refusal an org sees when enforcing would lock it out of itself.\n *\n * Says what to do, because a refusal that only says no sends someone to turn\n * the guard off. Deliberately free of a product name — this string is read by\n * self-host operators too.\n */\nexport const SSO_LOCKOUT_REFUSAL =\n 'Enforcing single sign-on would remove every sign-in method that does not ' +\n 'go through your identity provider, and this organization would be left ' +\n 'with no way in that survives your identity provider failing. If its ' +\n 'certificate lapses or its application is removed, nobody would be able ' +\n 'to sign in and we could not let you back in either. Give the ' +\n 'organization one of two things first: an owner who signs in outside your ' +\n 'identity pool, or a designated break-glass account inside it that keeps ' +\n 'a password.'\n\nexport default enforceSsoSignInMethods\n"],"names":["FieldValue","authForPool","findBreakGlassOrgOwners","invalidateTokenRevocationCache","firebaseAdmin","notifyUsers","firestore","app","TENANT_USER_CAP","SsoEnforcementError","Error","enforceSsoSignInMethods","orgId","options","actorUid","dryRun","force","db","orgSnapshot","collection","doc","get","exists","sso","tenantId","providerId","status","enforced","breakGlassUids","Array","isArray","map","String","filter","Boolean","pool","page","listUsers","accounts","changedUids","breakGlass","Set","record","users","summary","planAccount","push","ownerLookup","lockout","assessSsoLockoutRisk","owners","unavailable","safe","SSO_LOCKOUT_REFUSAL","find","user","uid","unlinked","length","skipped","updateUser","providersToUnlink","revokeRefreshTokens","add","action","target","before","providers","providerIdsOf","after","kept","tokensRevoked","at","serverTimestamp","type","title","body","link","scanned","changed","truncated","pageToken","providerData","info","id","email","has","ownersOutsidePool","ownerLookupFailed","designated","byUid","Map","account","retainedBy","ineffective","protects","some"],"mappings":"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"}
@@ -0,0 +1,320 @@
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 { DomainProbe } from './sso-drift-logic';
18
+ /** Subdomain the challenge record is published at. */
19
+ export declare const SSO_CHALLENGE_PREFIX = "_aglyn-challenge";
20
+ /** Prefix on the TXT value, so an unrelated record cannot pass by accident. */
21
+ export declare const SSO_TXT_PREFIX = "aglyn-domain-verification=";
22
+ export declare function normalizeSsoDomain(input: string): string;
23
+ /**
24
+ * `{ value, error }` rather than a discriminated union on purpose: an
25
+ * `{ ok: true } | { ok: false }` union does not narrow reliably once it crosses
26
+ * a library boundary in this repo (`strictNullChecks` is off), and the caller
27
+ * ends up unable to reach either arm's fields. Both keys always present, one of
28
+ * them always null.
29
+ */
30
+ export interface DomainCheck {
31
+ domain: string | null;
32
+ error: string | null;
33
+ }
34
+ export declare function validateSsoDomain(input: string): DomainCheck;
35
+ /**
36
+ * TXT records at the challenge host, lowercased and unwrapped.
37
+ *
38
+ * A TXT answer arrives as an array of string CHUNKS per record (DNS splits
39
+ * strings over 255 bytes), so each record is joined before comparison —
40
+ * otherwise a long token would never match.
41
+ */
42
+ export declare function resolveChallengeTxt(domain: string): Promise<string[]>;
43
+ /** The exact TXT value an org must publish for a domain. */
44
+ export declare function challengeValue(token: string): string;
45
+ /**
46
+ * Does this set of TXT records prove control of the zone for THIS token?
47
+ *
48
+ * Extracted so the comparison that domain verification rests on is reachable
49
+ * from a test without standing up Firestore. It is deliberately an exact match
50
+ * on a whole record: a substring test would accept
51
+ * `aglyn-domain-verification=<token>-and-more`, and a prefix test would accept
52
+ * any record that merely starts the same way. Neither proves anything.
53
+ */
54
+ export declare function recordsProveOwnership(records: readonly string[], token: string): boolean;
55
+ /**
56
+ * The pure half of re-verification — the probe STATES and the decision made
57
+ * from them — lives in `sso-drift-logic.ts` and is re-exported here so the
58
+ * public surface is one module. It has no imports at all, which is what lets
59
+ * the console route's spec run the REAL decision against faked I/O.
60
+ */
61
+ export { assessDomainDrift, SSO_DRIFT_FAILURES_BEFORE_REPORT, SSO_DRIFT_MIN_AGE_MS, type DomainDriftAction, type DomainDriftState, type DomainDriftVerdict, type DomainProbe, type DomainProbeStatus, } from './sso-drift-logic';
62
+ /**
63
+ * Re-check a claim's TXT record, distinguishing silence from absence.
64
+ *
65
+ * Same pinned public resolvers and same fallback as {@link resolveChallengeTxt}
66
+ * — this is not a second opinion about DNS, it is the same lookup reporting one
67
+ * more bit. NXDOMAIN/ENOTFOUND/ENODATA are ANSWERS ("that name has no TXT
68
+ * record") and stay conclusive; anything else from both the pinned resolvers
69
+ * and the runtime fallback is `unreachable`.
70
+ *
71
+ * Never throws: a sweep across every org must not stop at the first bad zone.
72
+ */
73
+ export declare function probeChallengeTxt(domain: string, token: string): Promise<DomainProbe>;
74
+ export interface SsoDomainClaimView {
75
+ domain: string;
76
+ token: string;
77
+ verified: boolean;
78
+ /** Fully-qualified host the record goes on. */
79
+ recordHost: string;
80
+ /** Exact value the record must carry. */
81
+ recordValue: string;
82
+ lastRecords?: string[];
83
+ }
84
+ /**
85
+ * Start (or re-read) a claim on a domain. Idempotent: an existing claim keeps
86
+ * its token, because reissuing would invalidate a record the customer may have
87
+ * already published and turn a working setup into a mysterious failure.
88
+ */
89
+ export declare function issueDomainClaim(orgId: string, rawDomain: string): Promise<{
90
+ claim: SsoDomainClaimView | null;
91
+ error: string | null;
92
+ }>;
93
+ export interface DomainVerifyResult {
94
+ domain: string;
95
+ verified: boolean;
96
+ /** What the lookup actually saw — the difference between "missing" and "wrong". */
97
+ records: string[];
98
+ expected: string;
99
+ }
100
+ /**
101
+ * Check a claim against live DNS and, on success, promote the domain into
102
+ * `sso.domains` — the array that sign-in consults.
103
+ *
104
+ * Failure is recorded but never destructive: a transient DNS outage must not
105
+ * silently un-govern a domain mid-flight. Removal is `revokeDomain`, which is
106
+ * an explicit act.
107
+ */
108
+ export declare function verifyDomainClaim(orgId: string, rawDomain: string): Promise<{
109
+ result: DomainVerifyResult | null;
110
+ error: string | null;
111
+ }>;
112
+ /**
113
+ * Drop a domain: out of `sso.domains`, and its public routing doc deactivated.
114
+ *
115
+ * Order matters. The public doc goes first, because it is the one an attacker
116
+ * would benefit from: while it says `active`, sign-ins for the domain still
117
+ * route to this org's IdP regardless of what the org doc says.
118
+ */
119
+ export declare function revokeDomain(orgId: string, rawDomain: string): Promise<void>;
120
+ /**
121
+ * What the customer needs to type into their IdP. Derived, never stored —
122
+ * these are properties of OUR deployment, and a stale stored copy would send
123
+ * somebody to configure an ACS URL we no longer serve.
124
+ *
125
+ * The callback is Firebase Auth's own handler on the project `authDomain`;
126
+ * `rpEntityId` is the relying-party entity id their IdP will see.
127
+ */
128
+ export interface SsoServiceMetadata {
129
+ acsUrl: string;
130
+ entityId: string;
131
+ authDomain: string;
132
+ }
133
+ /**
134
+ * Thrown by `ssoServiceMetadata()` when this deployment has no auth origin at
135
+ * all (AGL-2020).
136
+ *
137
+ * Deliberately names the variables and NOT a value: handing the operator
138
+ * a `*.firebaseapp.com` example here is how they end up pasting somebody
139
+ * else's project id into their IdP. `sso-provisioning.spec.ts` asserts this
140
+ * string mentions no Aglyn host.
141
+ */
142
+ export declare const SSO_AUTH_ORIGIN_UNCONFIGURED_MESSAGE: string;
143
+ /**
144
+ * The auth origin this deployment actually serves (AGL-1381).
145
+ *
146
+ * Mirrors `resolveFirebaseAuthDomain()` in the client's `firebase-config`: on a
147
+ * workspace deployment the whole OAuth/SAML handshake is funnelled through one
148
+ * dedicated same-site origin, `auth.<workspaceDomain>`, which reverse-proxies
149
+ * Firebase's `/__/auth/*` helpers (AGL-462). The IdP has to post the assertion
150
+ * to THAT origin — the one holding the pending-redirect state — or browser
151
+ * storage partitioning severs the two halves of the flow.
152
+ *
153
+ * The order is load-bearing:
154
+ *
155
+ * 1. `NEXT_PUBLIC_FIREBASE_AUTH_HANDLER_HOST` — an explicit override wins.
156
+ * 2. `auth.<NEXT_PUBLIC_WORKSPACE_DOMAIN>` — the deployed workspace origin.
157
+ * Derived only when the variable is actually set, never defaulted: falling
158
+ * back to `auth.aglyn.com` would point a self-hosted install at OUR auth
159
+ * origin, and it would also make the `firebaseapp.com` arm unreachable.
160
+ * It sits ABOVE `AUTH_DOMAIN` because production sets both, and the
161
+ * branded host is the one production serves.
162
+ * 3–4. the project's `*.firebaseapp.com` domain — localhost, previews that
163
+ * are not on the workspace domain, the emulator, and self-host installs
164
+ * that have not stood up an auth subdomain.
165
+ *
166
+ * When all four are absent this THROWS (AGL-2020). It used to default the
167
+ * project id to `aglyn-main` — ours — which is the anti-pattern AGL-1919
168
+ * exists to prevent, and it was silent in both directions that matter:
169
+ * `provisionSsoPool()` wrote our origin into the operator's OWN GCIP provider
170
+ * as `rpEntityId`/`callbackURL`, and the console printed it as the Reply URL
171
+ * for their customer to paste into their IdP. A third party's SAML assertions
172
+ * would then be posted at infrastructure the operator does not run.
173
+ *
174
+ * Throwing rather than degrading is deliberate, and this is the one place in
175
+ * the self-host audit where "run with the feature off" is not available: there
176
+ * is no empty value to fall back to. `authDomain: ''` emits
177
+ * `https:///__/auth/handler`, which GCIP accepts on write and which then
178
+ * rejects every assertion afterwards with nothing in the config that looks
179
+ * wrong — the same "looks configured, works never" shape as an App Check
180
+ * provider built on `undefined`. SSO is opt-in and deliberately configured, so
181
+ * an operator reaching this has asked for it; a named error telling them which
182
+ * variable to set is strictly better than either wrong origin.
183
+ *
184
+ * The READ path degrades instead of 500ing: `/api/orgs/sso?action=status`
185
+ * catches this and returns `metadata: null` with the message, so the settings
186
+ * page renders the reason rather than a wrong instruction. The WRITE path
187
+ * (`provisionSsoPool`) lets it fly.
188
+ *
189
+ * `entityId` carries the scheme. It is not cosmetic: live GCIP for the first
190
+ * SSO org holds `https://auth.aglyn.com`, and Google Workspace is already
191
+ * sending that exact Audience. Emitting the bare host here would be written
192
+ * over the working config by the next `provisionSsoPool()` save and lock out
193
+ * every SSO-only user in the org. `sso-provisioning.spec.ts` pins both strings
194
+ * character for character for that reason.
195
+ */
196
+ export declare function ssoServiceMetadata(): SsoServiceMetadata;
197
+ /**
198
+ * GCIP display names are constrained (4–20 chars, must start with a letter,
199
+ * letters/digits/hyphens only). An org slug can violate every one of those, so
200
+ * derive rather than pass through — a rejected display name would surface as an
201
+ * opaque provisioning failure at the worst moment.
202
+ */
203
+ export declare function poolDisplayName(orgSlug: string, orgId: string): string;
204
+ export interface SamlIdpMetadata {
205
+ entityId: string;
206
+ ssoUrl: string;
207
+ certificates: string[];
208
+ }
209
+ export interface ProvisionResult {
210
+ tenantId: string;
211
+ providerId: string;
212
+ /** True when this call created the pool rather than reusing one. */
213
+ createdPool: boolean;
214
+ /** True when this call created the provider rather than updating one. */
215
+ createdProvider: boolean;
216
+ }
217
+ /**
218
+ * Create (or update) the org's GCIP pool and its SAML provider.
219
+ *
220
+ * Idempotent by construction: an org that already has a `tenantId` reuses it,
221
+ * and a provider that already exists is UPDATED rather than recreated. Creating
222
+ * a second pool would strand every user in the first one — they are separate
223
+ * user namespaces, so "just make a new one" silently locks out everybody who
224
+ * has already signed in.
225
+ */
226
+ export declare function provisionSsoPool(options: {
227
+ orgId: string;
228
+ orgSlug: string;
229
+ existingTenantId?: string;
230
+ existingProviderId?: string;
231
+ displayName: string;
232
+ idp: SamlIdpMetadata;
233
+ }): Promise<ProvisionResult>;
234
+ /**
235
+ * Is this claim a STAFF ATTESTATION that the org owns the domain? (AGL-1887)
236
+ *
237
+ * The second of exactly two ways a domain can be proven, and the one that
238
+ * exists for orgs onboarded before self-serve. Their domain was written onto
239
+ * `sso.domains` by a person who checked, with no claim document behind it — so
240
+ * `unpublishSsoDomains` deactivated their routing happily and
241
+ * `publishSsoDomains` then refused to bring it back. Off worked, on did not,
242
+ * and for `aglyn-org` the owner's only credential lives inside the pool that
243
+ * stops answering (AGL-1888).
244
+ *
245
+ * A POSITIVE MARKER, never an absence. The rejected alternative was to treat
246
+ * "a routing doc already exists naming this org" as permission to re-publish,
247
+ * which makes `unpublish` non-final and would let a domain whose claim was
248
+ * revoked come back. What is trusted here is a field a staff member wrote on
249
+ * purpose, and it says WHO, so the trust is attributable.
250
+ *
251
+ * WHY THIS IS NOT A CLIENT-REACHABLE HOLE, which is the only question that
252
+ * matters about it:
253
+ *
254
+ * - `orgs/{orgId}/ssoDomains/{domain}` is `allow read, write: if false` in
255
+ * `cloud/firebase-firestore.rules` — deny-all for every client, reads
256
+ * included. Only the Admin SDK, which bypasses rules, can write here.
257
+ * - No customer-reachable route writes this field. `issueDomainClaim` is the
258
+ * only claim writer on the org's own path and it writes `domain`, `token`,
259
+ * `verified: false` and `createdAt` — never `attestedBy`.
260
+ * - {@link attestSsoDomain} is the only writer, and it is staff-only by
261
+ * construction: it lives here, server-side, and takes the attesting uid.
262
+ *
263
+ * That is why AGL-1887's proposed rules change is NOT part of this: the
264
+ * subcollection is already deny-all, and adding a rule permitting a narrower
265
+ * write than "none" would loosen it, not tighten it.
266
+ *
267
+ * A non-empty STRING, checked strictly. `true`, `1` or `{}` are not an
268
+ * attestation by anybody, and a field that can be satisfied by any truthy
269
+ * value is one a future careless write can satisfy by accident.
270
+ */
271
+ export declare function isStaffAttestedClaim(attestedBy: unknown): boolean;
272
+ /**
273
+ * Record that staff have verified, out of band, that an org owns a domain
274
+ * (AGL-1887).
275
+ *
276
+ * This is the write half of the attestation {@link isStaffAttestedClaim}
277
+ * reads, and it exists so there is exactly ONE way the marker can be created
278
+ * and it is a named function with an actor on it — rather than a field somebody
279
+ * sets by hand in the Firebase console with no record of who or why.
280
+ *
281
+ * `verified` is left FALSE and untouched. The two facts are different: DNS
282
+ * verification is a proof we re-ran ourselves, an attestation is a person
283
+ * vouching. Collapsing them would make an attested domain indistinguishable
284
+ * from a DNS-proven one in the data, and the `verified` flag is what
285
+ * `verifyDomainClaim` owns.
286
+ *
287
+ * Not exposed through `/api/orgs/sso`, deliberately. An org admin proving
288
+ * their own domain has DNS for that; an attestation is us saying we checked,
289
+ * and a customer-reachable path that wrote it would be a customer-reachable
290
+ * path to publishing any domain.
291
+ */
292
+ export declare function attestSsoDomain(options: {
293
+ orgId: string;
294
+ domain: string;
295
+ /** The staff member vouching. Recorded, and required. */
296
+ attestedByUid: string;
297
+ note?: string | null;
298
+ }): Promise<{
299
+ domain: string | null;
300
+ error: string | null;
301
+ }>;
302
+ /**
303
+ * Publish the public routing docs — the last step, and the one that actually
304
+ * turns SSO on for a domain.
305
+ *
306
+ * Only PROVEN domains are written — DNS-verified, or staff-attested per
307
+ * {@link isStaffAttestedClaim}. This function is the single writer of
308
+ * `ssoDomains`, so the invariant "a live routing doc implies proven ownership"
309
+ * is enforceable by reading one place.
310
+ */
311
+ export declare function publishSsoDomains(options: {
312
+ orgId: string;
313
+ tenantId: string;
314
+ providerId: string;
315
+ protocol: 'saml' | 'oidc';
316
+ displayName?: string;
317
+ domains: string[];
318
+ }): Promise<string[]>;
319
+ /** Deactivate every routing doc this org owns (disable, not delete). */
320
+ export declare function unpublishSsoDomains(orgId: string): Promise<void>;