create-safest-tools 0.2.0

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 (240) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +71 -0
  3. package/bin/create-safest-tools.mjs +12 -0
  4. package/package.json +34 -0
  5. package/src/cli.mjs +128 -0
  6. package/src/config.mjs +151 -0
  7. package/src/scaffold.mjs +30 -0
  8. package/template/LICENSE +201 -0
  9. package/template/README.md +35 -0
  10. package/template/console/README.md +28 -0
  11. package/template/console/analytics/AnalyticsWorkspace.tsx +55 -0
  12. package/template/console/analytics/controller.ts +133 -0
  13. package/template/console/analytics/events.ts +33 -0
  14. package/template/console/analytics/store.ts +33 -0
  15. package/template/console/analytics/types.ts +38 -0
  16. package/template/console/appeals/AppealWorkspace.tsx +73 -0
  17. package/template/console/appeals/controller.ts +125 -0
  18. package/template/console/appeals/events.ts +33 -0
  19. package/template/console/appeals/store.ts +35 -0
  20. package/template/console/appeals/types.ts +37 -0
  21. package/template/console/assistant/AssistantWorkspace.tsx +133 -0
  22. package/template/console/assistant/types.ts +34 -0
  23. package/template/console/auth/AccountJourney.tsx +69 -0
  24. package/template/console/auth/InvitationAcceptance.tsx +145 -0
  25. package/template/console/auth/OAuthButtons.tsx +78 -0
  26. package/template/console/auth/OperatorLogin.tsx +110 -0
  27. package/template/console/auth/PasswordRecovery.tsx +132 -0
  28. package/template/console/auth/account-route.ts +15 -0
  29. package/template/console/auth/browser-navigation.ts +7 -0
  30. package/template/console/auth/events.ts +15 -0
  31. package/template/console/command/CommandCentre.tsx +119 -0
  32. package/template/console/command/controller.ts +267 -0
  33. package/template/console/command/events.ts +19 -0
  34. package/template/console/command/store.ts +36 -0
  35. package/template/console/command/types.ts +61 -0
  36. package/template/console/components/AnalystIdentity.tsx +25 -0
  37. package/template/console/components/PageHeader.tsx +27 -0
  38. package/template/console/configuration/ConfigurationDialog.tsx +200 -0
  39. package/template/console/configuration/ConfigurationWorkspace.tsx +66 -0
  40. package/template/console/configuration/FormBuilder.tsx +143 -0
  41. package/template/console/configuration/api.ts +40 -0
  42. package/template/console/configuration/events.ts +14 -0
  43. package/template/console/configuration/types.ts +79 -0
  44. package/template/console/lib/format.ts +49 -0
  45. package/template/console/lib/http.ts +96 -0
  46. package/template/console/main.tsx +225 -0
  47. package/template/console/operations/OperationsWorkspace.tsx +126 -0
  48. package/template/console/operations/controller.ts +237 -0
  49. package/template/console/operations/events.ts +37 -0
  50. package/template/console/operations/store.ts +32 -0
  51. package/template/console/operations/types.ts +107 -0
  52. package/template/console/people/PeopleWorkspace.tsx +81 -0
  53. package/template/console/people/types.ts +25 -0
  54. package/template/console/profile/ProfileWorkspace.tsx +136 -0
  55. package/template/console/profile/events.ts +9 -0
  56. package/template/console/profile/types.ts +6 -0
  57. package/template/console/quality/QualityWorkspace.tsx +87 -0
  58. package/template/console/quality/controller.ts +154 -0
  59. package/template/console/quality/events.ts +34 -0
  60. package/template/console/quality/store.ts +36 -0
  61. package/template/console/quality/types.ts +70 -0
  62. package/template/console/queues/QueueEditor.tsx +173 -0
  63. package/template/console/queues/QueueWorkspace.tsx +109 -0
  64. package/template/console/queues/controller.ts +194 -0
  65. package/template/console/queues/events.ts +34 -0
  66. package/template/console/queues/store.ts +37 -0
  67. package/template/console/queues/types.ts +102 -0
  68. package/template/console/registry/RegistryDialog.tsx +198 -0
  69. package/template/console/registry/RegistryWorkspace.tsx +108 -0
  70. package/template/console/registry/events.ts +14 -0
  71. package/template/console/registry/types.ts +52 -0
  72. package/template/console/reports/ReportDrawer.tsx +159 -0
  73. package/template/console/reports/ReportWorkspace.tsx +90 -0
  74. package/template/console/reports/controller.ts +517 -0
  75. package/template/console/reports/events.ts +42 -0
  76. package/template/console/reports/store.ts +39 -0
  77. package/template/console/reports/types.ts +230 -0
  78. package/template/console/settings/BrandEditor.tsx +126 -0
  79. package/template/console/settings/ChannelDialog.tsx +241 -0
  80. package/template/console/settings/SettingsWorkspace.tsx +155 -0
  81. package/template/console/settings/events.ts +19 -0
  82. package/template/console/settings/handoff.ts +22 -0
  83. package/template/console/settings/types.ts +93 -0
  84. package/template/console/shell/WorkspaceShell.tsx +160 -0
  85. package/template/console/shell/controller.ts +182 -0
  86. package/template/console/shell/events.ts +25 -0
  87. package/template/console/shell/navigation.ts +61 -0
  88. package/template/console/shell/store.ts +56 -0
  89. package/template/console/shell/types.ts +86 -0
  90. package/template/console/workflows/CreateWorkflowDialog.tsx +87 -0
  91. package/template/console/workflows/WorkflowCanvas.tsx +42 -0
  92. package/template/console/workflows/WorkflowDialogs.tsx +6 -0
  93. package/template/console/workflows/WorkflowStudio.tsx +331 -0
  94. package/template/console/workflows/WorkflowWorkspace.tsx +82 -0
  95. package/template/console/workflows/events.ts +17 -0
  96. package/template/console/workflows/graph.ts +156 -0
  97. package/template/console/workflows/templates.ts +70 -0
  98. package/template/console/workflows/types.ts +201 -0
  99. package/template/gitignore.template +18 -0
  100. package/template/migrations/0001_reports_foundation.sql +444 -0
  101. package/template/migrations/0002_human_report_loop.sql +65 -0
  102. package/template/migrations/0003_delivery_reliability.sql +18 -0
  103. package/template/migrations/0004_public_intake.sql +14 -0
  104. package/template/migrations/0005_operations_visibility.sql +24 -0
  105. package/template/migrations/0006_ai_governance.sql +198 -0
  106. package/template/migrations/0007_ai_release_gates.sql +6 -0
  107. package/template/migrations/0008_retention_analytics_exports.sql +52 -0
  108. package/template/migrations/0009_retention_derived_copies.sql +14 -0
  109. package/template/migrations/0010_ai_quality_controls.sql +26 -0
  110. package/template/migrations/0011_analyst_presence.sql +28 -0
  111. package/template/migrations/0012_queue_policies.sql +87 -0
  112. package/template/migrations/0013_routing_agents.sql +63 -0
  113. package/template/migrations/0014_webhook_enrichments.sql +122 -0
  114. package/template/migrations/0015_queue_owned_ai.sql +55 -0
  115. package/template/migrations/0016_operator_accounts.sql +67 -0
  116. package/template/migrations/0017_operator_profiles_and_recovery.sql +33 -0
  117. package/template/migrations/0018_platform_configuration.sql +382 -0
  118. package/template/migrations/0019_workflow_authoring_runtime.sql +372 -0
  119. package/template/migrations/0020_tasks_findings_assistant_budgets.sql +427 -0
  120. package/template/migrations/0021_abuse_evidence_operations.sql +324 -0
  121. package/template/migrations/0022_workflow_dispatch_operations.sql +42 -0
  122. package/template/migrations/0023_component_connection_execution.sql +75 -0
  123. package/template/migrations/0024_access_runtime_integrity.sql +72 -0
  124. package/template/migrations/0025_installation_timezone.sql +11 -0
  125. package/template/migrations/0026_ai_and_egress_execution_controls.sql +49 -0
  126. package/template/migrations/0027_prompt_and_ai_registry.sql +37 -0
  127. package/template/migrations/0028_step_attempt_ai_provenance.sql +13 -0
  128. package/template/migrations/0029_evidence_fetch_transport.sql +5 -0
  129. package/template/migrations/0030_evidence_dlq_incidents.sql +45 -0
  130. package/template/migrations/0031_shadow_quality_integrity.sql +7 -0
  131. package/template/migrations/0032_action_delivery_outbox.sql +55 -0
  132. package/template/migrations/0033_configuration_and_assistant_drafts.sql +43 -0
  133. package/template/migrations/0034_installation_integrations.sql +31 -0
  134. package/template/migrations/0035_workspace_governance.sql +21 -0
  135. package/template/migrations/0036_published_routing_baseline.sql +15 -0
  136. package/template/migrations/0037_builtin_phishing_specialist.sql +71 -0
  137. package/template/migrations/0038_remove_deprecated_enrichment_runtime.sql +228 -0
  138. package/template/migrations/0039_secure_reporting_channels.sql +45 -0
  139. package/template/migrations/0040_notification_only_reporting.sql +24 -0
  140. package/template/migrations/0041_better_auth_credentials.sql +17 -0
  141. package/template/package.json +47 -0
  142. package/template/public/_headers +27 -0
  143. package/template/public/app-icon-192.png +0 -0
  144. package/template/public/app-icon-512.png +0 -0
  145. package/template/public/brand-icon.svg +7 -0
  146. package/template/public/brand-tokens.css +80 -0
  147. package/template/public/console/auth-shell.js +19340 -0
  148. package/template/public/customer-brand.js +58 -0
  149. package/template/public/embed/embed.css +139 -0
  150. package/template/public/embed/embed.js +408 -0
  151. package/template/public/embed/index.html +91 -0
  152. package/template/public/favicon.svg +7 -0
  153. package/template/public/fonts/Manrope-Variable.ttf +0 -0
  154. package/template/public/fonts/Newsreader-Italic-Variable.ttf +0 -0
  155. package/template/public/fonts/Newsreader-Variable.ttf +0 -0
  156. package/template/public/index.html +123 -0
  157. package/template/public/logo-primary.svg +7 -0
  158. package/template/public/logo-reversed.svg +7 -0
  159. package/template/public/manifest.webmanifest +21 -0
  160. package/template/public/public-report.js +184 -0
  161. package/template/public/report/index.html +39 -0
  162. package/template/public/report/public-report.css +20 -0
  163. package/template/public/social-card.png +0 -0
  164. package/template/public/styles.css +1521 -0
  165. package/template/public/widget.css +80 -0
  166. package/template/public/widget.js +220 -0
  167. package/template/reports.config.example.json +38 -0
  168. package/template/reports.schema.json +89 -0
  169. package/template/scripts/reports-auth-onboarding.mjs +180 -0
  170. package/template/scripts/reports-backup.mjs +276 -0
  171. package/template/scripts/reports-cloudflare-preflight.mjs +152 -0
  172. package/template/scripts/reports-deploy.mjs +173 -0
  173. package/template/scripts/reports-plan.mjs +226 -0
  174. package/template/scripts/reports-restore.mjs +180 -0
  175. package/template/scripts/reports-secrets.mjs +73 -0
  176. package/template/scripts/reports-uninstall-plan.mjs +32 -0
  177. package/template/src/agent-executor.ts +330 -0
  178. package/template/src/ai-observability.ts +163 -0
  179. package/template/src/ai-registry-validation.ts +244 -0
  180. package/template/src/ai-registry.ts +292 -0
  181. package/template/src/api-cursor.ts +84 -0
  182. package/template/src/assistant.ts +554 -0
  183. package/template/src/audit.ts +39 -0
  184. package/template/src/backup-service.ts +269 -0
  185. package/template/src/budget-control.ts +127 -0
  186. package/template/src/component-executor.ts +845 -0
  187. package/template/src/configuration-registry.ts +550 -0
  188. package/template/src/connection-egress.ts +430 -0
  189. package/template/src/connection-oauth.ts +368 -0
  190. package/template/src/evidence-service.ts +335 -0
  191. package/template/src/human-tasks.ts +316 -0
  192. package/template/src/index.ts +4114 -0
  193. package/template/src/installation-admin.ts +301 -0
  194. package/template/src/intake-abuse.ts +156 -0
  195. package/template/src/platform-registry-validation.ts +367 -0
  196. package/template/src/platform-registry.ts +753 -0
  197. package/template/src/report-admin.ts +342 -0
  198. package/template/src/report-ai-quality.ts +390 -0
  199. package/template/src/report-ai-validation.ts +80 -0
  200. package/template/src/report-ai.ts +857 -0
  201. package/template/src/report-auth.ts +292 -0
  202. package/template/src/report-better-auth.ts +583 -0
  203. package/template/src/report-context-schema.ts +132 -0
  204. package/template/src/report-context.ts +126 -0
  205. package/template/src/report-crypto.ts +142 -0
  206. package/template/src/report-delivery.ts +579 -0
  207. package/template/src/report-form-validation.ts +51 -0
  208. package/template/src/report-governance.ts +510 -0
  209. package/template/src/report-http.ts +90 -0
  210. package/template/src/report-operations.ts +685 -0
  211. package/template/src/report-operator-accounts.ts +810 -0
  212. package/template/src/report-presence.ts +398 -0
  213. package/template/src/report-queue-validation.ts +309 -0
  214. package/template/src/report-queues.ts +478 -0
  215. package/template/src/report-repository.ts +972 -0
  216. package/template/src/report-router-agent.ts +328 -0
  217. package/template/src/report-router-validation.ts +81 -0
  218. package/template/src/report-routing.ts +178 -0
  219. package/template/src/report-turnstile.ts +142 -0
  220. package/template/src/report-types.ts +224 -0
  221. package/template/src/report-validation.ts +279 -0
  222. package/template/src/report-workflow-validation.ts +119 -0
  223. package/template/src/report-workflow.ts +886 -0
  224. package/template/src/shadow-quality.ts +278 -0
  225. package/template/src/workflow-actions.ts +782 -0
  226. package/template/src/workflow-compiler.ts +230 -0
  227. package/template/src/workflow-dynamic-runtime.ts +613 -0
  228. package/template/src/workflow-effects.ts +316 -0
  229. package/template/src/workflow-expressions.ts +191 -0
  230. package/template/src/workflow-platform-types.ts +121 -0
  231. package/template/src/workflow-platform-validation.ts +540 -0
  232. package/template/src/workflow-repository.ts +916 -0
  233. package/template/src/workflow-runs.ts +686 -0
  234. package/template/src/workflow-simulator.ts +211 -0
  235. package/template/src/workspace-branding.ts +128 -0
  236. package/template/src/workspace-governance.ts +279 -0
  237. package/template/tsconfig.console.json +23 -0
  238. package/template/tsconfig.json +22 -0
  239. package/template/vite.console.config.ts +20 -0
  240. package/template/worker-configuration.d.ts +65 -0
@@ -0,0 +1,91 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Report a safety concern</title>
7
+ <link rel="stylesheet" href="/embed/embed.css" />
8
+ <script src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit" defer></script>
9
+ <script src="/embed/embed.js" type="module"></script>
10
+ </head>
11
+ <body>
12
+ <main class="report-card" id="report-card">
13
+ <header class="card-header">
14
+ <div class="brand" aria-label="Reporting service">
15
+ <img id="brand-logo" hidden />
16
+ <span id="brand-fallback" class="brand-fallback" aria-hidden="true">SR</span>
17
+ <span class="brand-copy"><strong id="brand-name">Safest Resolve</strong><small>Independent reporting channel</small></span>
18
+ </div>
19
+ <span class="privacy-mark"><span aria-hidden="true">●</span> Secure report</span>
20
+ </header>
21
+
22
+ <section id="loading-view" class="state-view loading-view" aria-live="polite">
23
+ <span class="spinner" aria-hidden="true"></span>
24
+ <strong>Preparing your report</strong>
25
+ <p>Connecting to the secure reporting channel…</p>
26
+ </section>
27
+
28
+ <section id="error-view" class="state-view error-view" hidden>
29
+ <span class="state-icon" aria-hidden="true">!</span>
30
+ <strong>We couldn’t open the report form</strong>
31
+ <p id="setup-error">Try again in a moment.</p>
32
+ <button id="retry-setup" class="secondary" type="button">Try again</button>
33
+ </section>
34
+
35
+ <form id="report-form" hidden novalidate>
36
+ <div class="intro">
37
+ <p class="eyebrow" id="mode-label">Safety report</p>
38
+ <h1 id="form-title">Report a safety concern</h1>
39
+ <p id="form-description">Tell the safety team what happened.</p>
40
+ </div>
41
+
42
+ <aside class="trust-note" id="trust-note">
43
+ <span class="trust-icon" aria-hidden="true">✓</span>
44
+ <div><strong id="trust-title">Sent securely to the safety team</strong><p id="trust-copy">Your answers go directly to the protected reporting service.</p></div>
45
+ </aside>
46
+
47
+ <section id="contact-section" class="contact-section" aria-labelledby="contact-heading" hidden>
48
+ <label for="reporter-email">
49
+ <span class="field-label"><strong id="contact-heading">Email for updates</strong><span>Required</span></span>
50
+ <input id="reporter-email" name="reporter-email" type="email" inputmode="email" autocomplete="email" maxlength="254" placeholder="you@example.com" />
51
+ <small>Used only to send report updates or ask for more information. It is not treated as proof of identity.</small>
52
+ </label>
53
+ </section>
54
+
55
+ <section class="form-section" aria-labelledby="reason-heading">
56
+ <div class="section-heading"><span>1</span><div><h2 id="reason-heading">What best describes the concern?</h2><p>Choose the closest option. A reviewer will read the details.</p></div></div>
57
+ <fieldset id="reason-options" class="reason-grid"><legend class="visually-hidden">Reason for reporting</legend></fieldset>
58
+ </section>
59
+
60
+ <section class="form-section" aria-labelledby="details-heading">
61
+ <div class="section-heading"><span>2</span><div><h2 id="details-heading">Help the reviewer understand</h2><p>Share only what is relevant. Don’t include passwords or payment details.</p></div></div>
62
+ <div id="answer-fields" class="answer-fields"></div>
63
+ </section>
64
+
65
+ <section class="form-section verification-section" aria-labelledby="verification-heading">
66
+ <div class="section-heading"><span>3</span><div><h2 id="verification-heading">Confirm and send</h2><p>A quick, privacy-friendly check helps stop automated spam. Most people won’t need to do anything.</p></div></div>
67
+ <div id="turnstile-mount" class="turnstile-mount"></div>
68
+ <p id="verification-status" class="verification-status" aria-live="polite">Human verification will run when you submit.</p>
69
+ <label class="consent-card"><input id="consent" type="checkbox" required /><span><strong>I’m ready to send this report</strong><small id="consent-notice"></small></span></label>
70
+ </section>
71
+
72
+ <p id="form-error" class="form-error" role="alert" tabindex="-1"></p>
73
+ <div class="form-actions">
74
+ <button id="cancel-report" class="quiet" type="button">Cancel</button>
75
+ <button id="submit-report" class="primary" type="submit"><span>Send report</span><span class="button-arrow" aria-hidden="true">→</span></button>
76
+ </div>
77
+ <p class="privacy-footer">Sent securely and stored in this organization’s Resolve service.</p>
78
+ </form>
79
+
80
+ <section id="success-view" class="state-view success-view" hidden tabindex="-1">
81
+ <span class="success-icon" aria-hidden="true">✓</span>
82
+ <p class="eyebrow">Report received</p>
83
+ <h1>Your report is with the safety team</h1>
84
+ <p id="receipt-copy">You can close this window now. Updates will be sent through the application’s notification channel.</p>
85
+ <div class="reference-card"><span>Your reference</span><strong id="receipt-reference"></strong><button id="copy-reference" type="button">Copy</button></div>
86
+ <p class="notification-reminder"><span aria-hidden="true">●</span> Keep the reference in case the safety team asks for it.</p>
87
+ <button id="finish-report" class="primary finish-button" type="button">Done</button>
88
+ </section>
89
+ </main>
90
+ </body>
91
+ </html>
@@ -0,0 +1,7 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-labelledby="title desc">
2
+ <title id="title">Safest app icon</title>
3
+ <desc id="desc">The Safest open-boundary mark on a deep evergreen tile.</desc>
4
+ <rect x="2" y="2" width="60" height="60" rx="17" fill="#0B2B20"/>
5
+ <path d="M 35.000 14.000 H 29.000 C 20.716 14.000 14.000 20.716 14.000 29.000 V 32.000" fill="none" stroke="#FFFFFF" stroke-width="11.500" stroke-linecap="round" stroke-linejoin="round"/>
6
+ <path d="M 29.000 50.000 H 35.000 C 43.284 50.000 50.000 43.284 50.000 35.000 V 32.000" fill="none" stroke="#CBEF68" stroke-width="11.500" stroke-linecap="round" stroke-linejoin="round"/>
7
+ </svg>
@@ -0,0 +1,123 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <meta name="description" content="Review reports, record decisions, send updates, and follow application actions and appeals on Cloudflare." />
7
+ <meta name="application-name" content="Safest Resolve" />
8
+ <meta name="theme-color" content="#123b2b" />
9
+ <title>Safest Resolve — Reports and workflows</title>
10
+ <link rel="icon" href="./favicon.svg" type="image/svg+xml" />
11
+ <link rel="manifest" href="./manifest.webmanifest" />
12
+ <link rel="stylesheet" href="./styles.css" />
13
+ </head>
14
+ <body class="login-mode">
15
+ <svg class="svg-defs" aria-hidden="true">
16
+ <defs>
17
+ <symbol id="i-home" viewBox="0 0 24 24"><path d="m3 10 9-7 9 7v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 21v-8h6v8"/></symbol>
18
+ <symbol id="i-inbox" viewBox="0 0 24 24"><path d="M4 4h16l2 10v5a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-5z"/><path d="M2 14h5l2 3h6l2-3h5"/></symbol>
19
+ <symbol id="i-queue" viewBox="0 0 24 24"><path d="M8 6h13M8 12h13M8 18h13"/><circle cx="3" cy="6" r="1"/><circle cx="3" cy="12" r="1"/><circle cx="3" cy="18" r="1"/></symbol>
20
+ <symbol id="i-workflow" viewBox="0 0 24 24"><rect x="3" y="3" width="6" height="6" rx="2"/><rect x="15" y="15" width="6" height="6" rx="2"/><path d="M9 6h3a3 3 0 0 1 3 3v6M12 18h3"/></symbol>
21
+ <symbol id="i-component" viewBox="0 0 24 24"><path d="m12 2 8 4.5v9L12 20l-8-4.5v-9z"/><path d="m4 6.5 8 4.5 8-4.5M12 11v9"/></symbol>
22
+ <symbol id="i-spark" viewBox="0 0 24 24"><path d="m12 3 1.5 5.5L19 10l-5.5 1.5L12 17l-1.5-5.5L5 10l5.5-1.5z"/><path d="m19 17 .8 2.2L22 20l-2.2.8L19 23l-.8-2.2L16 20l2.2-.8z"/></symbol>
23
+ <symbol id="i-quality" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="m8 12 2.5 2.5L16 9"/></symbol>
24
+ <symbol id="i-activity" viewBox="0 0 24 24"><path d="M3 12h4l2-7 4 14 2-7h6"/></symbol>
25
+ <symbol id="i-chart" viewBox="0 0 24 24"><path d="M4 20V10M10 20V4M16 20v-7M22 20H2"/></symbol>
26
+ <symbol id="i-settings" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.9l.1.1-2.8 2.8-.1-.1a1.7 1.7 0 0 0-1.9-.3 1.7 1.7 0 0 0-1 1.6v.2h-4V21a1.7 1.7 0 0 0-1-1.6 1.7 1.7 0 0 0-1.9.3l-.1.1L4.2 17l.1-.1a1.7 1.7 0 0 0 .3-1.9A1.7 1.7 0 0 0 3 14H2.8v-4H3a1.7 1.7 0 0 0 1.6-1 1.7 1.7 0 0 0-.3-1.9L4.2 7 7 4.2l.1.1A1.7 1.7 0 0 0 9 4.6 1.7 1.7 0 0 0 10 3V2.8h4V3a1.7 1.7 0 0 0 1 1.6 1.7 1.7 0 0 0 1.9-.3l.1-.1L19.8 7l-.1.1a1.7 1.7 0 0 0-.3 1.9 1.7 1.7 0 0 0 1.6 1h.2v4H21a1.7 1.7 0 0 0-1.6 1z"/></symbol>
27
+ <symbol id="i-people" viewBox="0 0 24 24"><circle cx="9" cy="8" r="4"/><path d="M2 21a7 7 0 0 1 14 0M16 4a4 4 0 0 1 0 8M18 14a6 6 0 0 1 4 7"/></symbol>
28
+ <symbol id="i-user" viewBox="0 0 24 24"><circle cx="12" cy="8" r="4"/><path d="M4 21a8 8 0 0 1 16 0"/></symbol>
29
+ <symbol id="i-appeal" viewBox="0 0 24 24"><path d="M7 7h10a4 4 0 0 1 4 4v6"/><path d="m7 3-4 4 4 4M17 21l4-4-4-4"/></symbol>
30
+ <symbol id="i-external" viewBox="0 0 24 24"><path d="M14 3h7v7M10 14 21 3M21 14v5a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5"/></symbol>
31
+ <symbol id="i-search" viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="m20 20-4-4"/></symbol>
32
+ <symbol id="i-menu" viewBox="0 0 24 24"><path d="M4 7h16M4 12h16M4 17h16"/></symbol>
33
+ <symbol id="i-plus" viewBox="0 0 24 24"><path d="M12 5v14M5 12h14"/></symbol>
34
+ <symbol id="i-alert" viewBox="0 0 24 24"><path d="M10.3 3.6 2.4 18a2 2 0 0 0 1.8 3h15.6a2 2 0 0 0 1.8-3L13.7 3.6a2 2 0 0 0-3.4 0z"/><path d="M12 9v4M12 17h.01"/></symbol>
35
+ </defs>
36
+ </svg>
37
+ <a class="skip-link" href="#main">Skip to workspace</a>
38
+ <div id="workspace-shell-root">
39
+ <header class="topbar">
40
+ <a class="brand" href="/" aria-label="Workspace home"><img src="./brand-icon.svg" data-customer-logo alt="Safest Resolve logo" width="34" height="34" /><span><strong data-customer-brand-name>Safest Resolve</strong><small>Reports and workflows</small></span></a>
41
+ <div class="top-actions"><span class="health"><i aria-hidden="true"></i><span>Checking storage</span></span></div>
42
+ </header>
43
+ </div>
44
+
45
+ <main id="main">
46
+ <section id="login" class="login-layout" aria-labelledby="login-title">
47
+ <div class="login-story">
48
+ <p class="eyebrow light">Safest Resolve</p>
49
+ <h1>Every report gets<br />a real next step.</h1>
50
+ <p>Sign in to review reports, record decisions, send updates, and follow actions and appeals.</p>
51
+ <ul>
52
+ <li>Data stays in your Cloudflare account</li>
53
+ <li>Protected and uncertain cases go to people</li>
54
+ <li>Decisions, notices, and actions remain attributable</li>
55
+ </ul>
56
+ </div>
57
+ <div class="login-card">
58
+ <div id="operator-login-root"><p class="auth-loading" role="status">Preparing secure sign in…</p></div>
59
+ <p class="privacy-note">This deployment stores its own accounts, sessions, reports, and audit history.</p>
60
+ </div>
61
+ </section>
62
+
63
+ <section id="workspace" class="workspace" hidden>
64
+ <div id="workspace-navigation-root"></div>
65
+
66
+ <div class="content">
67
+ <section id="command" class="command-view" data-view="command">
68
+ <div id="command-centre-root"><div class="skeleton-block" role="status" aria-label="Loading Command centre"></div></div>
69
+ </section>
70
+
71
+ <section id="reports" class="queue-view" data-view="reports">
72
+ <div id="report-workspace-root"><div class="skeleton-block" role="status" aria-label="Loading reports"></div></div>
73
+ </section>
74
+
75
+ <section id="queues" class="view-stack" data-view="queues" hidden><div id="queue-workspace-root"><div class="skeleton-block" role="status" aria-label="Loading queues and policies"></div></div></section>
76
+
77
+ <section id="workflows" class="view-stack" data-view="workflows" hidden><div id="workflow-workspace-root"><div class="skeleton-block" role="status" aria-label="Loading workflows"></div></div></section>
78
+
79
+ <section id="components" class="view-stack" data-view="components" hidden><div id="registry-workspace-root"><div class="skeleton-block" role="status" aria-label="Loading components"></div></div></section>
80
+
81
+ <section id="configuration" class="builder-view" data-view="configuration" hidden><div id="configuration-workspace-root"><div class="skeleton-block" role="status" aria-label="Loading configuration"></div></div></section>
82
+
83
+ <section id="assistant" class="view-stack" data-view="assistant" hidden><div id="assistant-workspace-root"><div class="skeleton-block" role="status" aria-label="Loading assistant"></div></div></section>
84
+
85
+ <section id="settings" class="view-stack" data-view="settings" hidden><div id="settings-workspace-root"><div class="skeleton-block" role="status" aria-label="Loading settings"></div></div></section>
86
+
87
+ <section id="people" class="view-stack" data-view="people" hidden><div id="people-workspace-root"><div class="skeleton-block" role="status" aria-label="Loading people and roles"></div></div></section>
88
+
89
+ <section id="profile" class="view-stack" data-view="profile" hidden><div id="profile-workspace-root"><div class="skeleton-block" role="status" aria-label="Loading profile"></div></div></section>
90
+
91
+ <section id="analytics" class="view-stack" data-view="analytics" hidden><div id="analytics-workspace-root"><div class="skeleton-block" role="status" aria-label="Loading reports and outcomes"></div></div></section>
92
+
93
+ <section id="appeals" class="view-stack" data-view="appeals" hidden><div id="appeal-workspace-root"><div class="skeleton-block" role="status" aria-label="Loading appeals"></div></div></section>
94
+
95
+ <section id="operations" class="view-stack" data-view="operations" hidden><div id="operations-workspace-root"><div class="skeleton-block" role="status" aria-label="Loading operations"></div></div></section>
96
+
97
+ <section id="ai-quality" class="view-stack" data-view="ai-quality" hidden><div id="quality-workspace-root"><div class="skeleton-block" role="status" aria-label="Loading AI quality samples"></div></div></section>
98
+ </div>
99
+ </section>
100
+ </main>
101
+
102
+ <div id="queue-editor-root"></div>
103
+
104
+ <div id="workflow-dialogs-root"></div>
105
+
106
+ <div id="registry-dialog-root"></div>
107
+
108
+ <div id="configuration-dialog-root"></div>
109
+
110
+ <div id="channel-dialog-root"></div>
111
+
112
+ <div id="report-drawer-root"></div>
113
+
114
+ <div id="appeal-dialog-root"></div>
115
+
116
+ <div id="quality-dialog-root"></div>
117
+
118
+ <div id="operations-dialog-root"></div>
119
+ <script src="./customer-brand.js"></script>
120
+ <script src="./widget.js"></script>
121
+ <script src="./console/auth-shell.js" type="module"></script>
122
+ </body>
123
+ </html>
@@ -0,0 +1,7 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 233.950 64" role="img" aria-labelledby="title desc">
2
+ <title id="title">Safest logo</title>
3
+ <desc id="desc">The Safest open-boundary symbol and wordmark.</desc>
4
+ <path d="M 35.000 14.000 H 29.000 C 20.716 14.000 14.000 20.716 14.000 29.000 V 32.000" fill="none" stroke="#154E3A" stroke-width="11.500" stroke-linecap="round" stroke-linejoin="round"/>
5
+ <path d="M 29.000 50.000 H 35.000 C 43.284 50.000 50.000 43.284 50.000 35.000 V 32.000" fill="none" stroke="#CBEF68" stroke-width="11.500" stroke-linecap="round" stroke-linejoin="round"/>
6
+ <path fill="#14231C" d="M94.99002685546876 48.25Q91.0133544921875 48.25 87.820849609375 46.85249633789063Q84.6283447265625 45.45499267578125 82.57584228515626 42.84831848144531Q80.52333984375 40.241644287109374 79.9666748046875 36.63663330078125L86.7334228515625 35.6299560546875Q87.49841918945313 38.7699462890625 89.90340576171874 40.47660522460937Q92.30839233398437 42.18326416015625 95.4100341796875 42.18326416015625Q97.18334350585937 42.18326416015625 98.80999755859375 41.62660522460938Q100.43665161132813 41.0699462890625 101.46831359863282 39.99245910644531Q102.4999755859375 38.914971923828126 102.4999755859375 37.3533203125Q102.4999755859375 36.71834106445313 102.30331726074219 36.15835266113281Q102.10665893554688 35.5983642578125 101.68750305175782 35.12753601074219Q101.26834716796876 34.656707763671875 100.55168762207032 34.25337524414063Q99.83502807617188 33.850042724609374 98.79669189453125 33.53004150390625L89.47001953125 30.7833740234375Q88.3600341796875 30.465045166015624 86.97086791992189 29.89671630859375Q85.58170166015626 29.328387451171874 84.30836181640626 28.30172119140625Q83.03502197265625 27.275054931640625 82.20084838867189 25.616714477539062Q81.3666748046875 23.9583740234375 81.3666748046875 21.46002197265625Q81.3666748046875 17.893341064453125 83.16918029785157 15.510836791992187Q84.97168579101563 13.128332519531249 87.99502563476562 11.95083312988281Q91.01836547851562 10.77333374023437 94.703369140625 10.796667480468749Q98.4150390625 10.843334960937497 101.33170776367187 12.073333740234371Q104.24837646484374 13.303332519531246 106.21087951660155 15.642498779296872Q108.17338256835937 17.9816650390625 109.02005615234376 21.343334960937497L102.019970703125 22.53668212890625Q101.62330322265625 20.728363037109375 100.51997680664061 19.475875854492188Q99.416650390625 18.223388671875 97.8699981689453 17.566729736328124Q96.32334594726562 16.91007080078125 94.6100341796875 16.8634033203125Q92.92838745117187 16.816735839843748 91.46756591796876 17.336727905273435Q90.00674438476562 17.856719970703125 89.09841613769531 18.83004150390625Q88.190087890625 19.803363037109374 88.190087890625 21.130017089843747Q88.190087890625 22.370001220703124 88.94424743652343 23.144158935546873Q89.69840698242187 23.918316650390622 90.83673095703125 24.385812377929685Q91.97505493164063 24.85330810546875 93.150048828125 25.1633056640625L99.480029296875 26.89996337890625Q100.85003662109375 27.26829833984375 102.52837524414062 27.875799560546874Q104.2067138671875 28.483300781249998 105.75421752929688 29.56496887207031Q107.30172119140624 30.646636962890625 108.30588989257812 32.412472534179685Q109.31005859375 34.17830810546875 109.31005859375 36.8866455078125Q109.31005859375 39.748321533203125 108.12005310058593 41.891659545898435Q106.93004760742187 44.03499755859375 104.90670776367188 45.44166564941406Q102.88336791992188 46.848333740234374 100.31502990722657 47.54916687011719Q97.74669189453125 48.25 94.99002685546876 48.25Z M121.29832763671875 48.25Q118.38166503906251 48.25 116.36499938964845 47.13833312988281Q114.34833374023438 46.026666259765626 113.3116668701172 44.168331909179685Q112.275 42.30999755859375 112.275 40.0699951171875Q112.275 38.161669921875 112.88082580566407 36.610836791992185Q113.48665161132813 35.060003662109374 114.79997863769532 33.891665649414065Q116.11330566406251 32.72332763671875 118.27164306640626 31.94998779296875Q119.8282958984375 31.404986572265624 121.94411926269532 30.984152221679686Q124.05994262695313 30.563317871093748 126.66161499023438 30.184982299804688Q129.26328735351564 29.806646728515624 132.2783203125 29.35330810546875L129.9416259765625 30.6466552734375Q129.94995727539063 28.088360595703122 128.75246887207032 26.88254699707031Q127.55498046875 25.6767333984375 124.7216796875 25.6767333984375Q123.09504394531251 25.6767333984375 121.44088439941407 26.454220581054685Q119.78672485351564 27.231707763671874 119.12506103515625 29.16668701171875L113.2316650390625 27.306665039062498Q114.24833374023439 23.930004882812497 117.07083435058595 21.84000244140625Q119.89333496093751 19.75 124.72834472656251 19.75Q128.31500854492188 19.75 131.07167968750002 20.878326416015625Q133.82835083007814 22.00665283203125 135.218359375 24.69998779296875Q135.98836669921877 26.16666259765625 136.13836669921875 27.661663818359372Q136.28836669921876 29.1566650390625 136.28836669921876 30.97333984375V47.5H130.60496826171877V41.82669677734375L131.4649658203125 42.90338134765625Q129.54995117187502 45.703363037109376 127.2016326904297 46.976681518554685Q124.85331420898439 48.25 121.29832763671875 48.25ZM122.64501953125 43.11661376953125Q124.59000244140626 43.11661376953125 125.94082031250001 42.428286743164065Q127.29163818359376 41.739959716796875 128.09579467773438 40.814144897460935Q128.89995117187502 39.888330078125 129.1816162109375 39.16669921875Q129.6849548339844 38.0467041015625 129.76662292480472 36.57503967285156Q129.84829101562502 35.10337524414062 129.84829101562502 34.1467041015625L131.7883056640625 34.67003173828125Q128.876611328125 35.14503784179688 126.97078552246094 35.47420654296875Q125.06495971679688 35.803375244140625 123.81831665039063 36.082540893554686Q122.57167358398438 36.36170654296875 121.61502685546876 36.70003662109375Q120.60003051757813 37.09337158203125 119.94170837402345 37.56836853027344Q119.28338623046875 38.043365478515625 118.95589599609374 38.63502197265625Q118.62840576171875 39.226678466796876 118.62840576171875 39.97999267578125Q118.62840576171875 40.929986572265626 119.10340270996093 41.63664245605469Q119.57839965820312 42.34329833984375 120.4742218017578 42.7299560546875Q121.3700439453125 43.11661376953125 122.64501953125 43.11661376953125Z M144.09332275390625 47.5V20.39005126953125Q144.09332275390625 19.363385009765622 144.14915466308594 18.090878295898435Q144.20498657226562 16.818371582031247 144.5666534423828 15.531695556640624Q144.9283203125 14.245019531250001 145.87999267578127 13.196679687500001Q147.086669921875 11.848333740234374 148.5008483886719 11.34499816894531Q149.91502685546877 10.841662597656246 151.2583679199219 10.795831298828123Q152.60170898437502 10.75 153.5833740234375 10.75H156.94337158203126V16.01005859375H153.8333740234375Q152.18338623046876 16.01005859375 151.38172607421876 16.843386840820312Q150.58006591796877 17.676715087890624 150.58006591796877 18.91002197265625V47.5ZM139.8 25.540051269531247V20.5H156.94337158203126V25.540051269531247Z M172.38669433593748 48.25Q168.25835571289062 48.25 165.1225158691406 46.46666564941406Q161.9866760253906 44.68333129882812 160.2183380126953 41.54166564941406Q158.45 38.4 158.45 34.32667236328125Q158.45 29.90167236328125 160.18250427246093 26.631671142578124Q161.91500854492188 23.361669921875 164.96918029785155 21.5558349609375Q168.02335205078123 19.75 172.0133544921875 19.75Q176.2383605957031 19.75 179.1983642578125 21.73333740234375Q182.15836791992186 23.7166748046875 183.5858673095703 27.3216796875Q185.01336669921875 30.9266845703125 184.61669311523437 35.8133544921875H178.18995361328123V33.4333251953125Q178.1799560546875 29.19501953125 176.7824768066406 27.2958740234375Q175.38499755859374 25.396728515625 172.293359375 25.396728515625Q168.73004760742185 25.396728515625 167.02506408691403 27.580877685546874Q165.32008056640623 29.76502685546875 165.32008056640623 34.0Q165.32008056640623 37.90830078125 167.02506408691403 40.045782470703124Q168.73004760742185 42.18326416015625 172.0133544921875 42.18326416015625Q174.10333251953125 42.18326416015625 175.60164794921874 41.26160888671875Q177.09996337890624 40.33995361328125 177.899951171875 38.60330810546875L184.36336669921874 40.489990234375Q182.8300354003906 44.17332763671875 179.56003112792968 46.211663818359376Q176.29002685546874 48.25 172.38669433593748 48.25ZM163.30006103515623 35.8133544921875V30.95997314453125H181.47332763671875V35.8133544921875Z M199.7283447265625 48.24666748046875Q194.66333618164066 48.24666748046875 191.53333435058596 45.96082763671875Q188.40333251953126 43.67498779296875 187.72500000000002 39.506640625L194.35507812500003 38.49996337890625Q194.79674072265627 40.43995361328125 196.3275573730469 41.543280029296874Q197.85837402343753 42.6466064453125 200.19835205078127 42.6466064453125Q202.1699890136719 42.6466064453125 203.24747009277348 41.87662048339844Q204.32495117187503 41.106634521484374 204.32495117187503 39.73665771484375Q204.32495117187503 38.891668701171874 203.90828857421877 38.374176025390625Q203.49162597656252 37.856683349609376 202.07080078125 37.361688232421876Q200.6499755859375 36.866693115234376 197.67498779296878 36.07669677734375Q194.338330078125 35.21336669921875 192.35249938964847 34.16753234863282Q190.3666687011719 33.121697998046876 189.4925018310547 31.6741943359375Q188.61833496093752 30.226690673828124 188.61833496093752 28.17001953125Q188.61833496093752 25.60001220703125 189.94000244140625 23.70500793457031Q191.261669921875 21.810003662109374 193.66333923339846 20.780001831054687Q196.0650085449219 19.75 199.31834716796877 19.75Q202.48001708984378 19.75 204.9258514404297 20.725833129882812Q207.37168579101564 21.701666259765624 208.8808532714844 23.499166870117186Q210.39002075195316 25.29666748046875 210.73168945312503 27.736669921875L204.10161132812502 28.930017089843748Q203.90661621093753 27.341705322265625 202.69580383300786 26.41588439941406Q201.48499145507816 25.490063476562497 199.44835205078127 25.350061035156248Q197.47337646484377 25.230059814453124 196.26922607421875 25.908380126953126Q195.06507568359376 26.586700439453125 195.06507568359376 27.836682128906247Q195.06507568359376 28.575006103515623 195.5684051513672 29.079165649414062Q196.07173461914064 29.5833251953125 197.62673034667972 30.09998779296875Q199.18172607421877 30.616650390625 202.33505859375003 31.416650390624998Q205.4750427246094 32.23164672851563 207.3642028808594 33.293313598632814Q209.2533630371094 34.35498046875 210.10752868652347 35.836648559570314Q210.96169433593752 37.31831665039063 210.96169433593752 39.416650390625Q210.96169433593752 43.53499145507813 207.97835693359377 45.89082946777344Q204.99501953125002 48.24666748046875 199.7283447265625 48.24666748046875Z M231.52503662109376 47.5Q228.7766906738281 48.02000122070312 226.13667907714841 47.954168701171874Q223.49666748046874 47.888336181640625 221.42165832519532 47.00416870117188Q219.34664916992188 46.120001220703124 218.268310546875 44.163330078125Q217.294970703125 42.348321533203126 217.24330444335936 40.47247924804688Q217.19163818359374 38.59663696289063 217.19163818359374 36.2166259765625V13.0H223.67838134765626V35.8432861328125Q223.67838134765626 37.42994995117188 223.71588134765625 38.67995300292969Q223.75338134765624 39.9299560546875 224.22838134765624 40.6932861328125Q225.12838134765624 42.143280029296875 227.10171508789062 42.28744201660156Q229.075048828125 42.43160400390625 231.52503662109376 42.09327392578125ZM212.475 25.540051269531247V20.5H231.52503662109376V25.540051269531247Z"/>
7
+ </svg>
@@ -0,0 +1,7 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 233.950 64" role="img" aria-labelledby="title desc">
2
+ <title id="title">Safest reversed logo</title>
3
+ <desc id="desc">The Safest symbol and wordmark for deep evergreen backgrounds.</desc>
4
+ <path d="M 35.000 14.000 H 29.000 C 20.716 14.000 14.000 20.716 14.000 29.000 V 32.000" fill="none" stroke="#FFFFFF" stroke-width="11.500" stroke-linecap="round" stroke-linejoin="round"/>
5
+ <path d="M 29.000 50.000 H 35.000 C 43.284 50.000 50.000 43.284 50.000 35.000 V 32.000" fill="none" stroke="#CBEF68" stroke-width="11.500" stroke-linecap="round" stroke-linejoin="round"/>
6
+ <path fill="#FFFFFF" d="M94.99002685546876 48.25Q91.0133544921875 48.25 87.820849609375 46.85249633789063Q84.6283447265625 45.45499267578125 82.57584228515626 42.84831848144531Q80.52333984375 40.241644287109374 79.9666748046875 36.63663330078125L86.7334228515625 35.6299560546875Q87.49841918945313 38.7699462890625 89.90340576171874 40.47660522460937Q92.30839233398437 42.18326416015625 95.4100341796875 42.18326416015625Q97.18334350585937 42.18326416015625 98.80999755859375 41.62660522460938Q100.43665161132813 41.0699462890625 101.46831359863282 39.99245910644531Q102.4999755859375 38.914971923828126 102.4999755859375 37.3533203125Q102.4999755859375 36.71834106445313 102.30331726074219 36.15835266113281Q102.10665893554688 35.5983642578125 101.68750305175782 35.12753601074219Q101.26834716796876 34.656707763671875 100.55168762207032 34.25337524414063Q99.83502807617188 33.850042724609374 98.79669189453125 33.53004150390625L89.47001953125 30.7833740234375Q88.3600341796875 30.465045166015624 86.97086791992189 29.89671630859375Q85.58170166015626 29.328387451171874 84.30836181640626 28.30172119140625Q83.03502197265625 27.275054931640625 82.20084838867189 25.616714477539062Q81.3666748046875 23.9583740234375 81.3666748046875 21.46002197265625Q81.3666748046875 17.893341064453125 83.16918029785157 15.510836791992187Q84.97168579101563 13.128332519531249 87.99502563476562 11.95083312988281Q91.01836547851562 10.77333374023437 94.703369140625 10.796667480468749Q98.4150390625 10.843334960937497 101.33170776367187 12.073333740234371Q104.24837646484374 13.303332519531246 106.21087951660155 15.642498779296872Q108.17338256835937 17.9816650390625 109.02005615234376 21.343334960937497L102.019970703125 22.53668212890625Q101.62330322265625 20.728363037109375 100.51997680664061 19.475875854492188Q99.416650390625 18.223388671875 97.8699981689453 17.566729736328124Q96.32334594726562 16.91007080078125 94.6100341796875 16.8634033203125Q92.92838745117187 16.816735839843748 91.46756591796876 17.336727905273435Q90.00674438476562 17.856719970703125 89.09841613769531 18.83004150390625Q88.190087890625 19.803363037109374 88.190087890625 21.130017089843747Q88.190087890625 22.370001220703124 88.94424743652343 23.144158935546873Q89.69840698242187 23.918316650390622 90.83673095703125 24.385812377929685Q91.97505493164063 24.85330810546875 93.150048828125 25.1633056640625L99.480029296875 26.89996337890625Q100.85003662109375 27.26829833984375 102.52837524414062 27.875799560546874Q104.2067138671875 28.483300781249998 105.75421752929688 29.56496887207031Q107.30172119140624 30.646636962890625 108.30588989257812 32.412472534179685Q109.31005859375 34.17830810546875 109.31005859375 36.8866455078125Q109.31005859375 39.748321533203125 108.12005310058593 41.891659545898435Q106.93004760742187 44.03499755859375 104.90670776367188 45.44166564941406Q102.88336791992188 46.848333740234374 100.31502990722657 47.54916687011719Q97.74669189453125 48.25 94.99002685546876 48.25Z M121.29832763671875 48.25Q118.38166503906251 48.25 116.36499938964845 47.13833312988281Q114.34833374023438 46.026666259765626 113.3116668701172 44.168331909179685Q112.275 42.30999755859375 112.275 40.0699951171875Q112.275 38.161669921875 112.88082580566407 36.610836791992185Q113.48665161132813 35.060003662109374 114.79997863769532 33.891665649414065Q116.11330566406251 32.72332763671875 118.27164306640626 31.94998779296875Q119.8282958984375 31.404986572265624 121.94411926269532 30.984152221679686Q124.05994262695313 30.563317871093748 126.66161499023438 30.184982299804688Q129.26328735351564 29.806646728515624 132.2783203125 29.35330810546875L129.9416259765625 30.6466552734375Q129.94995727539063 28.088360595703122 128.75246887207032 26.88254699707031Q127.55498046875 25.6767333984375 124.7216796875 25.6767333984375Q123.09504394531251 25.6767333984375 121.44088439941407 26.454220581054685Q119.78672485351564 27.231707763671874 119.12506103515625 29.16668701171875L113.2316650390625 27.306665039062498Q114.24833374023439 23.930004882812497 117.07083435058595 21.84000244140625Q119.89333496093751 19.75 124.72834472656251 19.75Q128.31500854492188 19.75 131.07167968750002 20.878326416015625Q133.82835083007814 22.00665283203125 135.218359375 24.69998779296875Q135.98836669921877 26.16666259765625 136.13836669921875 27.661663818359372Q136.28836669921876 29.1566650390625 136.28836669921876 30.97333984375V47.5H130.60496826171877V41.82669677734375L131.4649658203125 42.90338134765625Q129.54995117187502 45.703363037109376 127.2016326904297 46.976681518554685Q124.85331420898439 48.25 121.29832763671875 48.25ZM122.64501953125 43.11661376953125Q124.59000244140626 43.11661376953125 125.94082031250001 42.428286743164065Q127.29163818359376 41.739959716796875 128.09579467773438 40.814144897460935Q128.89995117187502 39.888330078125 129.1816162109375 39.16669921875Q129.6849548339844 38.0467041015625 129.76662292480472 36.57503967285156Q129.84829101562502 35.10337524414062 129.84829101562502 34.1467041015625L131.7883056640625 34.67003173828125Q128.876611328125 35.14503784179688 126.97078552246094 35.47420654296875Q125.06495971679688 35.803375244140625 123.81831665039063 36.082540893554686Q122.57167358398438 36.36170654296875 121.61502685546876 36.70003662109375Q120.60003051757813 37.09337158203125 119.94170837402345 37.56836853027344Q119.28338623046875 38.043365478515625 118.95589599609374 38.63502197265625Q118.62840576171875 39.226678466796876 118.62840576171875 39.97999267578125Q118.62840576171875 40.929986572265626 119.10340270996093 41.63664245605469Q119.57839965820312 42.34329833984375 120.4742218017578 42.7299560546875Q121.3700439453125 43.11661376953125 122.64501953125 43.11661376953125Z M144.09332275390625 47.5V20.39005126953125Q144.09332275390625 19.363385009765622 144.14915466308594 18.090878295898435Q144.20498657226562 16.818371582031247 144.5666534423828 15.531695556640624Q144.9283203125 14.245019531250001 145.87999267578127 13.196679687500001Q147.086669921875 11.848333740234374 148.5008483886719 11.34499816894531Q149.91502685546877 10.841662597656246 151.2583679199219 10.795831298828123Q152.60170898437502 10.75 153.5833740234375 10.75H156.94337158203126V16.01005859375H153.8333740234375Q152.18338623046876 16.01005859375 151.38172607421876 16.843386840820312Q150.58006591796877 17.676715087890624 150.58006591796877 18.91002197265625V47.5ZM139.8 25.540051269531247V20.5H156.94337158203126V25.540051269531247Z M172.38669433593748 48.25Q168.25835571289062 48.25 165.1225158691406 46.46666564941406Q161.9866760253906 44.68333129882812 160.2183380126953 41.54166564941406Q158.45 38.4 158.45 34.32667236328125Q158.45 29.90167236328125 160.18250427246093 26.631671142578124Q161.91500854492188 23.361669921875 164.96918029785155 21.5558349609375Q168.02335205078123 19.75 172.0133544921875 19.75Q176.2383605957031 19.75 179.1983642578125 21.73333740234375Q182.15836791992186 23.7166748046875 183.5858673095703 27.3216796875Q185.01336669921875 30.9266845703125 184.61669311523437 35.8133544921875H178.18995361328123V33.4333251953125Q178.1799560546875 29.19501953125 176.7824768066406 27.2958740234375Q175.38499755859374 25.396728515625 172.293359375 25.396728515625Q168.73004760742185 25.396728515625 167.02506408691403 27.580877685546874Q165.32008056640623 29.76502685546875 165.32008056640623 34.0Q165.32008056640623 37.90830078125 167.02506408691403 40.045782470703124Q168.73004760742185 42.18326416015625 172.0133544921875 42.18326416015625Q174.10333251953125 42.18326416015625 175.60164794921874 41.26160888671875Q177.09996337890624 40.33995361328125 177.899951171875 38.60330810546875L184.36336669921874 40.489990234375Q182.8300354003906 44.17332763671875 179.56003112792968 46.211663818359376Q176.29002685546874 48.25 172.38669433593748 48.25ZM163.30006103515623 35.8133544921875V30.95997314453125H181.47332763671875V35.8133544921875Z M199.7283447265625 48.24666748046875Q194.66333618164066 48.24666748046875 191.53333435058596 45.96082763671875Q188.40333251953126 43.67498779296875 187.72500000000002 39.506640625L194.35507812500003 38.49996337890625Q194.79674072265627 40.43995361328125 196.3275573730469 41.543280029296874Q197.85837402343753 42.6466064453125 200.19835205078127 42.6466064453125Q202.1699890136719 42.6466064453125 203.24747009277348 41.87662048339844Q204.32495117187503 41.106634521484374 204.32495117187503 39.73665771484375Q204.32495117187503 38.891668701171874 203.90828857421877 38.374176025390625Q203.49162597656252 37.856683349609376 202.07080078125 37.361688232421876Q200.6499755859375 36.866693115234376 197.67498779296878 36.07669677734375Q194.338330078125 35.21336669921875 192.35249938964847 34.16753234863282Q190.3666687011719 33.121697998046876 189.4925018310547 31.6741943359375Q188.61833496093752 30.226690673828124 188.61833496093752 28.17001953125Q188.61833496093752 25.60001220703125 189.94000244140625 23.70500793457031Q191.261669921875 21.810003662109374 193.66333923339846 20.780001831054687Q196.0650085449219 19.75 199.31834716796877 19.75Q202.48001708984378 19.75 204.9258514404297 20.725833129882812Q207.37168579101564 21.701666259765624 208.8808532714844 23.499166870117186Q210.39002075195316 25.29666748046875 210.73168945312503 27.736669921875L204.10161132812502 28.930017089843748Q203.90661621093753 27.341705322265625 202.69580383300786 26.41588439941406Q201.48499145507816 25.490063476562497 199.44835205078127 25.350061035156248Q197.47337646484377 25.230059814453124 196.26922607421875 25.908380126953126Q195.06507568359376 26.586700439453125 195.06507568359376 27.836682128906247Q195.06507568359376 28.575006103515623 195.5684051513672 29.079165649414062Q196.07173461914064 29.5833251953125 197.62673034667972 30.09998779296875Q199.18172607421877 30.616650390625 202.33505859375003 31.416650390624998Q205.4750427246094 32.23164672851563 207.3642028808594 33.293313598632814Q209.2533630371094 34.35498046875 210.10752868652347 35.836648559570314Q210.96169433593752 37.31831665039063 210.96169433593752 39.416650390625Q210.96169433593752 43.53499145507813 207.97835693359377 45.89082946777344Q204.99501953125002 48.24666748046875 199.7283447265625 48.24666748046875Z M231.52503662109376 47.5Q228.7766906738281 48.02000122070312 226.13667907714841 47.954168701171874Q223.49666748046874 47.888336181640625 221.42165832519532 47.00416870117188Q219.34664916992188 46.120001220703124 218.268310546875 44.163330078125Q217.294970703125 42.348321533203126 217.24330444335936 40.47247924804688Q217.19163818359374 38.59663696289063 217.19163818359374 36.2166259765625V13.0H223.67838134765626V35.8432861328125Q223.67838134765626 37.42994995117188 223.71588134765625 38.67995300292969Q223.75338134765624 39.9299560546875 224.22838134765624 40.6932861328125Q225.12838134765624 42.143280029296875 227.10171508789062 42.28744201660156Q229.075048828125 42.43160400390625 231.52503662109376 42.09327392578125ZM212.475 25.540051269531247V20.5H231.52503662109376V25.540051269531247Z"/>
7
+ </svg>
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "Safest Resolve",
3
+ "short_name": "Safest Resolve",
4
+ "description": "Trust & safety operations, built for Cloudflare.",
5
+ "start_url": "/",
6
+ "display": "standalone",
7
+ "background_color": "#F5F6F1",
8
+ "theme_color": "#0B2B20",
9
+ "icons": [
10
+ {
11
+ "src": "/app-icon-192.png",
12
+ "sizes": "192x192",
13
+ "type": "image/png"
14
+ },
15
+ {
16
+ "src": "/app-icon-512.png",
17
+ "sizes": "512x512",
18
+ "type": "image/png"
19
+ }
20
+ ]
21
+ }
@@ -0,0 +1,184 @@
1
+ const form = document.querySelector("#public-report-form");
2
+ const setupError = document.querySelector("#setup-error");
3
+ const formError = document.querySelector("#form-error");
4
+ const idempotencyKey = crypto.randomUUID();
5
+ let config;
6
+ let turnstileToken = "";
7
+
8
+ function option(value, label) {
9
+ const element = document.createElement("option");
10
+ element.value = value;
11
+ element.textContent = label;
12
+ return element;
13
+ }
14
+
15
+ function readable(value) {
16
+ const text = String(value || "").replaceAll("_", " ").replaceAll("-", " ");
17
+ return text ? `${text[0].toUpperCase()}${text.slice(1)}` : "";
18
+ }
19
+
20
+ function fieldLabel(definition) {
21
+ const span = document.createElement("span");
22
+ span.textContent = definition.label || readable(definition.key);
23
+ if (definition.required) {
24
+ const required = document.createElement("span");
25
+ required.className = "required-marker";
26
+ required.setAttribute("aria-hidden", "true");
27
+ required.textContent = " *";
28
+ span.append(required);
29
+ }
30
+ return span;
31
+ }
32
+
33
+ function renderField(definition) {
34
+ const key = String(definition.key);
35
+ const wrapper = document.createElement(definition.type === "multi_select" ? "fieldset" : "label");
36
+ wrapper.dataset.answerKey = key;
37
+ if (definition.type === "multi_select") {
38
+ const legend = document.createElement("legend");
39
+ legend.append(fieldLabel(definition));
40
+ wrapper.append(legend);
41
+ } else wrapper.append(fieldLabel(definition));
42
+ if (definition.help_text) {
43
+ const help = document.createElement("small");
44
+ help.textContent = definition.help_text;
45
+ wrapper.append(help);
46
+ }
47
+ let control;
48
+ if (definition.type === "long_text") control = document.createElement("textarea");
49
+ else if (definition.type === "select" || definition.type === "boolean") {
50
+ control = document.createElement("select");
51
+ control.append(option("", "Choose an option"));
52
+ if (definition.type === "boolean") control.append(option("true", "Yes"), option("false", "No"));
53
+ else for (const choice of definition.options || []) control.append(option(choice.value, choice.label));
54
+ } else if (definition.type === "multi_select") {
55
+ control = document.createElement("div");
56
+ control.className = "public-choices";
57
+ for (const choice of definition.options || []) {
58
+ const label = document.createElement("label");
59
+ label.className = "public-choice";
60
+ const input = document.createElement("input");
61
+ input.type = "checkbox";
62
+ input.name = `answer-${key}`;
63
+ input.value = choice.value;
64
+ label.append(input, document.createTextNode(choice.label));
65
+ control.append(label);
66
+ }
67
+ } else {
68
+ control = document.createElement("input");
69
+ control.type = definition.type === "number" ? "number" : definition.type === "url" ? "url" : "text";
70
+ }
71
+ control.dataset.answerControl = key;
72
+ if (control instanceof HTMLInputElement || control instanceof HTMLTextAreaElement || control instanceof HTMLSelectElement) {
73
+ control.name = `answer-${key}`;
74
+ control.required = definition.required === true;
75
+ if (definition.placeholder) control.placeholder = definition.placeholder;
76
+ if (definition.max_length) control.maxLength = Number(definition.max_length);
77
+ if (definition.type === "number") {
78
+ if (definition.min_value !== undefined) control.min = String(definition.min_value);
79
+ if (definition.max_value !== undefined) control.max = String(definition.max_value);
80
+ }
81
+ }
82
+ wrapper.append(control);
83
+ return wrapper;
84
+ }
85
+
86
+ function collectAnswers() {
87
+ const answers = {};
88
+ for (const definition of config.form.fields || []) {
89
+ const key = String(definition.key);
90
+ const wrapper = document.querySelector(`[data-answer-key="${CSS.escape(key)}"]`);
91
+ let value;
92
+ if (definition.type === "multi_select") value = [...wrapper.querySelectorAll("input:checked")].map((input) => input.value);
93
+ else {
94
+ const control = wrapper.querySelector("[data-answer-control]");
95
+ if (definition.type === "number") value = control.value === "" ? undefined : control.valueAsNumber;
96
+ else if (definition.type === "boolean") value = control.value === "" ? undefined : control.value === "true";
97
+ else value = control.value.trim();
98
+ }
99
+ const missing = value === undefined || value === "" || Array.isArray(value) && value.length === 0;
100
+ if (definition.required && missing) {
101
+ wrapper.querySelector("input, select, textarea")?.focus();
102
+ throw new Error(`${definition.label || readable(key)} is required.`);
103
+ }
104
+ if (!missing) answers[key] = value;
105
+ }
106
+ return answers;
107
+ }
108
+
109
+ async function waitForTurnstile() {
110
+ for (let attempts = 0; attempts < 100; attempts += 1) {
111
+ if (typeof globalThis.turnstile?.render === "function") return globalThis.turnstile;
112
+ await new Promise((resolve) => setTimeout(resolve, 50));
113
+ }
114
+ throw new Error("The anti-abuse check could not be loaded.");
115
+ }
116
+
117
+ try {
118
+ const requestedForm = new URLSearchParams(location.search).get("form") || "general-v1";
119
+ const response = await fetch(`/v1/public/config?form_version=${encodeURIComponent(requestedForm)}`, { headers: { accept: "application/json" }, credentials: "omit" });
120
+ const result = await response.json().catch(() => ({}));
121
+ if (!response.ok) throw new Error(result?.error?.message || "Public reporting is unavailable.");
122
+ config = result;
123
+ globalThis.SafestBrand?.applyBrand(config.brand, { title: config.form.title || "Report a safety concern" });
124
+ document.querySelector("#title").textContent = config.form.title;
125
+ document.querySelector("#description").textContent = config.form.description;
126
+ document.querySelector("#consent-notice").textContent = config.form.consent_notice;
127
+ document.querySelector("#target-type").replaceChildren(...config.form.target_types.map((type) => option(type, readable(type))));
128
+ document.querySelector("#reason").replaceChildren(...config.form.reasons.map((reason) => option(reason.code, reason.label)));
129
+ document.querySelector("#answer-fields").replaceChildren(...(config.form.fields || []).map(renderField));
130
+ const turnstile = await waitForTurnstile();
131
+ turnstile.render("#turnstile-widget", {
132
+ sitekey: config.turnstile_site_key,
133
+ action: "report_submit",
134
+ callback: (token) => { turnstileToken = token; },
135
+ "expired-callback": () => { turnstileToken = ""; },
136
+ "error-callback": () => { turnstileToken = ""; },
137
+ });
138
+ form.hidden = false;
139
+ } catch (error) {
140
+ setupError.textContent = error instanceof Error ? error.message : "Public reporting is unavailable.";
141
+ }
142
+
143
+ form.addEventListener("submit", async (event) => {
144
+ event.preventDefault();
145
+ formError.textContent = "";
146
+ const button = form.querySelector("button");
147
+ if (!turnstileToken) {
148
+ formError.textContent = "Complete the anti-abuse check before submitting.";
149
+ return;
150
+ }
151
+ let answers;
152
+ try { answers = collectAnswers(); }
153
+ catch (error) { formError.textContent = error instanceof Error ? error.message : "Complete the required fields."; return; }
154
+ const evidenceUrl = document.querySelector("#evidence-url").value.trim();
155
+ const timestamp = new Date().toISOString();
156
+ button.disabled = true;
157
+ try {
158
+ const response = await fetch("/v1/reports/public", {
159
+ method: "POST",
160
+ headers: { "content-type": "application/json" },
161
+ credentials: "omit",
162
+ referrerPolicy: "no-referrer",
163
+ body: JSON.stringify({
164
+ schema_version: "1", idempotency_key: idempotencyKey, source: "public_page", form_version: config.form.id,
165
+ reporter: { email: document.querySelector("#reporter-email").value.trim() },
166
+ target: { type: document.querySelector("#target-type").value, reference: document.querySelector("#target-reference").value },
167
+ reason_code: document.querySelector("#reason").value, answers,
168
+ evidence: { mode: "reference_only", references: evidenceUrl ? [evidenceUrl] : [] },
169
+ locale: document.documentElement.lang || "en",
170
+ consent: { notice: config.form.consent_notice, accepted_at: timestamp }, submitted_at: timestamp,
171
+ turnstile_token: turnstileToken,
172
+ }),
173
+ });
174
+ const result = await response.json().catch(() => ({}));
175
+ if (!response.ok) throw new Error(result?.error?.message || "The report could not be accepted.");
176
+ form.hidden = true;
177
+ document.querySelector("#receipt-reference").textContent = `Keep reference ${result.reference} for your records.`;
178
+ document.querySelector("#receipt-notification").textContent = `Updates will be sent to ${document.querySelector("#reporter-email").value.trim().toLowerCase()}.`;
179
+ document.querySelector("#receipt").hidden = false;
180
+ } catch (error) {
181
+ formError.textContent = error instanceof Error ? error.message : "The report could not be accepted.";
182
+ button.disabled = false;
183
+ }
184
+ });
@@ -0,0 +1,39 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <meta name="referrer" content="no-referrer" />
7
+ <title>Report a safety concern</title>
8
+ <link rel="icon" href="../favicon.svg" type="image/svg+xml" />
9
+ <link rel="stylesheet" href="../styles.css" />
10
+ <link rel="stylesheet" href="./public-report.css" />
11
+ <script src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit" defer></script>
12
+ </head>
13
+ <body>
14
+ <header class="public-top"><a class="brand" href="/report/" aria-label="Public safety report"><img data-customer-logo hidden alt="" width="38" height="38" /><span class="customer-brand-fallback" data-customer-logo-fallback data-customer-brand-fallback>SR</span><span><strong data-customer-brand-name>Safest Resolve</strong><small>Public safety report</small></span></a></header>
15
+ <main class="public-main">
16
+ <article class="public-card">
17
+ <p class="eyebrow">Public intake</p>
18
+ <h1 id="title">Report a safety concern</h1>
19
+ <p id="description">Use this page when you cannot report from inside the application or need to report the service itself.</p>
20
+ <form id="public-report-form" class="public-form" hidden>
21
+ <label>What are you reporting?<select id="target-type" required></select></label>
22
+ <label>Target reference<input id="target-reference" maxlength="500" required placeholder="A username, listing ID, URL slug, or service name" /></label>
23
+ <label>Email for updates <small>Required so the safety team can send updates or ask for more information. It is not treated as proof of identity.</small><input id="reporter-email" type="email" inputmode="email" autocomplete="email" maxlength="254" required placeholder="you@example.com" /></label>
24
+ <label>Reason<select id="reason" required></select></label>
25
+ <div id="answer-fields" class="dynamic-fields"></div>
26
+ <label>Evidence URL <small>Optional. Only HTTPS links are accepted; no file upload is created here.</small><input id="evidence-url" type="url" maxlength="2048" placeholder="https://example.com/reference" /></label>
27
+ <label class="consent"><input id="consent" type="checkbox" required /><span id="consent-notice"></span></label>
28
+ <div id="turnstile-widget"></div>
29
+ <button class="primary" type="submit">Submit report</button>
30
+ <p id="form-error" class="error" role="alert"></p>
31
+ </form>
32
+ <div id="receipt" class="receipt" hidden><h2>Report received</h2><p id="receipt-reference"></p><p id="receipt-notification"></p></div>
33
+ <p id="setup-error" class="error" role="alert"></p>
34
+ </article>
35
+ </main>
36
+ <script src="../customer-brand.js"></script>
37
+ <script src="../public-report.js" type="module"></script>
38
+ </body>
39
+ </html>
@@ -0,0 +1,20 @@
1
+ body { background: var(--canvas); }
2
+ .public-top { height: 66px; padding: 0 22px; display: flex; align-items: center; border-bottom: 1px solid var(--line); background: white; }
3
+ .public-main { width: min(700px, calc(100% - 28px)); margin: 42px auto; }
4
+ .public-card { padding: clamp(24px, 5vw, 42px); border: 1px solid var(--line); border-radius: max(8px, var(--customer-radius)); background: var(--paper); box-shadow: var(--safest-shadow-sm); }
5
+ .public-card > p:not(.eyebrow) { color: var(--muted); font-size: 11px; line-height: 1.6; }
6
+ .public-form { display: grid; gap: 13px; }
7
+ .dynamic-fields { display: grid; gap: 13px; }
8
+ .dynamic-fields fieldset { margin: 0; padding: 0; border: 0; }
9
+ .dynamic-fields legend { margin-bottom: 7px; padding: 0; color: #314139; font-size: 10px; font-weight: 800; }
10
+ .required-marker { color: var(--danger); }
11
+ .public-form textarea { min-height: 130px; padding: 10px; border: 1px solid #cfd7d1; border-radius: 8px; resize: vertical; font: inherit; }
12
+ .public-choices { display: grid; gap: 7px; }
13
+ .public-choice { min-height: 42px; padding: 9px 10px; display: flex; grid-template-columns: none; gap: 8px; align-items: flex-start; border: 1px solid var(--line); border-radius: max(6px, calc(var(--customer-radius) * .65)); font-size: 11px; font-weight: 550; }
14
+ .public-choice:has(input:checked) { border-color: var(--forest); background: color-mix(in srgb, var(--forest) 7%, var(--paper)); }
15
+ .public-choice input { width: auto; min-height: 0; margin-top: 2px; accent-color: var(--forest); }
16
+ .public-form .consent { grid-template-columns: auto 1fr; align-items: start; font-weight: 500; line-height: 1.5; }
17
+ .public-form .consent input { width: auto; min-height: 0; }
18
+ .public-form button { margin-top: 5px; }
19
+ .receipt { padding: 18px; border-left: 4px solid var(--forest); border-radius: 9px; background: #edf6f0; }
20
+ .receipt p { margin-bottom: 0; color: var(--muted); line-height: 1.55; }
Binary file