@iann29/rastro 0.1.0-alpha.0 → 0.1.0-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/README.md +579 -57
  2. package/agent/integration.md +661 -0
  3. package/agent/manifest.json +187 -0
  4. package/agent/manifest.schema.json +411 -0
  5. package/dist/client/federation.d.ts +236 -0
  6. package/dist/client/federation.d.ts.map +1 -0
  7. package/dist/client/federation.js +196 -0
  8. package/dist/client/federation.js.map +1 -0
  9. package/dist/client/index.d.ts +2467 -18
  10. package/dist/client/index.d.ts.map +1 -1
  11. package/dist/client/index.js +461 -32
  12. package/dist/client/index.js.map +1 -1
  13. package/dist/component/_generated/api.d.ts +18 -0
  14. package/dist/component/_generated/api.d.ts.map +1 -1
  15. package/dist/component/_generated/api.js.map +1 -1
  16. package/dist/component/_generated/component.d.ts +302 -4
  17. package/dist/component/_generated/component.d.ts.map +1 -1
  18. package/dist/component/_generated/server.d.ts +4 -0
  19. package/dist/component/_generated/server.d.ts.map +1 -1
  20. package/dist/component/_generated/server.js.map +1 -1
  21. package/dist/component/affiliates.d.ts.map +1 -1
  22. package/dist/component/affiliates.js +6 -2
  23. package/dist/component/affiliates.js.map +1 -1
  24. package/dist/component/cardinality.d.ts +12 -0
  25. package/dist/component/cardinality.d.ts.map +1 -0
  26. package/dist/component/cardinality.js +94 -0
  27. package/dist/component/cardinality.js.map +1 -0
  28. package/dist/component/constants.d.ts +34 -7
  29. package/dist/component/constants.d.ts.map +1 -1
  30. package/dist/component/constants.js +47 -7
  31. package/dist/component/constants.js.map +1 -1
  32. package/dist/component/convex.config.d.ts +6 -1
  33. package/dist/component/convex.config.js +9 -1
  34. package/dist/component/convex.config.js.map +1 -1
  35. package/dist/component/coverage.d.ts +19 -0
  36. package/dist/component/coverage.d.ts.map +1 -0
  37. package/dist/component/coverage.js +30 -0
  38. package/dist/component/coverage.js.map +1 -0
  39. package/dist/component/diagnostics.d.ts +9 -0
  40. package/dist/component/diagnostics.d.ts.map +1 -0
  41. package/dist/component/diagnostics.js +47 -0
  42. package/dist/component/diagnostics.js.map +1 -0
  43. package/dist/component/errors.d.ts +1 -1
  44. package/dist/component/errors.d.ts.map +1 -1
  45. package/dist/component/errors.js.map +1 -1
  46. package/dist/component/eventStore.d.ts +106 -0
  47. package/dist/component/eventStore.d.ts.map +1 -0
  48. package/dist/component/eventStore.js +294 -0
  49. package/dist/component/eventStore.js.map +1 -0
  50. package/dist/component/funnels.d.ts.map +1 -1
  51. package/dist/component/funnels.js +5 -3
  52. package/dist/component/funnels.js.map +1 -1
  53. package/dist/component/geo.d.ts +71 -0
  54. package/dist/component/geo.d.ts.map +1 -0
  55. package/dist/component/geo.js +611 -0
  56. package/dist/component/geo.js.map +1 -0
  57. package/dist/component/goals.d.ts.map +1 -1
  58. package/dist/component/goals.js +25 -9
  59. package/dist/component/goals.js.map +1 -1
  60. package/dist/component/guards.d.ts.map +1 -1
  61. package/dist/component/guards.js.map +1 -1
  62. package/dist/component/http.d.ts.map +1 -1
  63. package/dist/component/http.js +284 -53
  64. package/dist/component/http.js.map +1 -1
  65. package/dist/component/identity.d.ts +13 -0
  66. package/dist/component/identity.d.ts.map +1 -0
  67. package/dist/component/identity.js +58 -0
  68. package/dist/component/identity.js.map +1 -0
  69. package/dist/component/ingest.d.ts +3 -1
  70. package/dist/component/ingest.d.ts.map +1 -1
  71. package/dist/component/ingest.js +1105 -243
  72. package/dist/component/ingest.js.map +1 -1
  73. package/dist/component/live.d.ts +8 -0
  74. package/dist/component/live.d.ts.map +1 -1
  75. package/dist/component/live.js +74 -9
  76. package/dist/component/live.js.map +1 -1
  77. package/dist/component/reports.d.ts +273 -24
  78. package/dist/component/reports.d.ts.map +1 -1
  79. package/dist/component/reports.js +1320 -150
  80. package/dist/component/reports.js.map +1 -1
  81. package/dist/component/retention.d.ts +75 -1
  82. package/dist/component/retention.d.ts.map +1 -1
  83. package/dist/component/retention.js +541 -31
  84. package/dist/component/retention.js.map +1 -1
  85. package/dist/component/sanitize.d.ts +24 -1
  86. package/dist/component/sanitize.d.ts.map +1 -1
  87. package/dist/component/sanitize.js +107 -19
  88. package/dist/component/sanitize.js.map +1 -1
  89. package/dist/component/schema.d.ts +462 -55
  90. package/dist/component/schema.js +195 -15
  91. package/dist/component/schema.js.map +1 -1
  92. package/dist/component/sites.d.ts.map +1 -1
  93. package/dist/component/sites.js +38 -25
  94. package/dist/component/sites.js.map +1 -1
  95. package/dist/component/useragent.d.ts +9 -0
  96. package/dist/component/useragent.d.ts.map +1 -0
  97. package/dist/component/useragent.js +152 -0
  98. package/dist/component/useragent.js.map +1 -0
  99. package/dist/component/validators.d.ts +101 -16
  100. package/dist/component/validators.d.ts.map +1 -1
  101. package/dist/component/validators.js +19 -2
  102. package/dist/component/validators.js.map +1 -1
  103. package/dist/component/visitors.d.ts +19 -0
  104. package/dist/component/visitors.d.ts.map +1 -0
  105. package/dist/component/visitors.js +86 -0
  106. package/dist/component/visitors.js.map +1 -0
  107. package/dist/component/vitals.d.ts +41 -0
  108. package/dist/component/vitals.d.ts.map +1 -0
  109. package/dist/component/vitals.js +115 -0
  110. package/dist/component/vitals.js.map +1 -0
  111. package/dist/react/index.d.ts.map +1 -1
  112. package/dist/react/index.js.map +1 -1
  113. package/dist/tracker/generated.d.ts +8 -4
  114. package/dist/tracker/generated.d.ts.map +1 -1
  115. package/dist/tracker/generated.js +8 -4
  116. package/dist/tracker/generated.js.map +1 -1
  117. package/dist/tracker/tracker.d.ts +1 -1
  118. package/dist/tracker/tracker.d.ts.map +1 -1
  119. package/dist/tracker/tracker.js +81 -33
  120. package/dist/tracker/tracker.js.map +1 -1
  121. package/dist/tracker/vitals.d.ts +10 -0
  122. package/dist/tracker/vitals.d.ts.map +1 -0
  123. package/dist/tracker/vitals.js +140 -0
  124. package/dist/tracker/vitals.js.map +1 -0
  125. package/dist/tracker.min.js +1 -1
  126. package/dist/vitals.min.js +1 -0
  127. package/docs/benchmarks/2026-08-20-realistic.md +171 -0
  128. package/docs/benchmarks/2026-08-21-formal-certification.md +353 -0
  129. package/docs/benchmarks/2026-08-30-alpha6-recertification.md +206 -0
  130. package/docs/federation-setup.md +397 -0
  131. package/docs/federation.md +262 -0
  132. package/docs/upgrading.md +259 -0
  133. package/llms.txt +71 -0
  134. package/package.json +61 -11
  135. package/scripts/benchmark-ingest.mjs +601 -0
  136. package/scripts/generate-federation-keys.mjs +20 -0
  137. package/src/component/_generated/api.ts +18 -0
  138. package/src/component/_generated/component.ts +366 -4
  139. package/src/component/_generated/server.ts +4 -0
  140. package/src/component/affiliates.ts +20 -5
  141. package/src/component/cardinality.ts +117 -0
  142. package/src/component/constants.ts +47 -7
  143. package/src/component/convex.config.ts +11 -1
  144. package/src/component/coverage.ts +36 -0
  145. package/src/component/diagnostics.ts +65 -0
  146. package/src/component/errors.ts +2 -1
  147. package/src/component/eventStore.ts +489 -0
  148. package/src/component/funnels.ts +19 -16
  149. package/src/component/geo.ts +781 -0
  150. package/src/component/goals.ts +45 -16
  151. package/src/component/guards.ts +3 -1
  152. package/src/component/http.ts +403 -60
  153. package/src/component/identity.ts +74 -0
  154. package/src/component/ingest.ts +1652 -291
  155. package/src/component/live.ts +90 -9
  156. package/src/component/reports.ts +1929 -199
  157. package/src/component/retention.ts +774 -97
  158. package/src/component/sanitize.ts +154 -32
  159. package/src/component/schema.ts +219 -15
  160. package/src/component/sites.ts +56 -28
  161. package/src/component/useragent.ts +171 -0
  162. package/src/component/validators.ts +29 -1
  163. package/src/component/visitors.ts +116 -0
  164. package/src/component/vitals.ts +146 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vitals.js","sourceRoot":"","sources":["../../src/tracker/vitals.ts"],"names":[],"mappings":";AAAA,8EAA8E;AAC9E,6EAA6E;AAC7E,6EAA6E;AAc7E,CAAC,GAAG,EAAE;IACJ,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAyC,CAAC;IAClE,MAAM,MAAM,GAAG,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC;IACpC,MAAM,QAAQ,GACZ,MAAM,EAAE,OAAO,CAAC,QAAQ;QACxB,CAAC,MAAM,EAAE,GAAG,IAAI,IAAI,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;IACtD,IAAI,CAAC,MAAM,IAAI,CAAC,QAAQ;QAAE,OAAO;IAEjC,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC;IACnE,IAAI,SAAiB,CAAC;IACtB,IAAI,CAAC;QACH,SAAS,GAAG,cAAc,CAAC,EAAE,IAAI,QAAQ,EAAE,CAAC;QAC5C,cAAc,CAAC,EAAE,GAAG,SAAS,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,SAAS,GAAG,QAAQ,EAAE,CAAC;IACzB,CAAC;IACD,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,IAAI,SAAS,CAAC;IACtD,2EAA2E;IAC3E,qCAAqC;IACrC,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAE7C,MAAM,MAAM,GAAuC,EAAE,CAAC;IACtD,MAAM,SAAS,GAGV,EAAE,CAAC;IACR,MAAM,OAAO,GAAG,CACd,IAAY,EACZ,OAA8C,EAC9C,KAA+B,EAC/B,EAAE;QACF,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,IAAI,mBAAmB,CAAC,CAAC,IAAI,EAAE,EAAE,CAChD,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAC3B,CAAC;YACF,QAAQ,CAAC,OAAO,CAAC;gBACf,IAAI;gBACJ,QAAQ,EAAE,IAAI;gBACd,GAAG,KAAK;aACkB,CAAC,CAAC;YAC9B,SAAS,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;QACxC,CAAC;QAAC,MAAM,CAAC;YACP,qEAAqE;QACvE,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,UAAU,GAAG,WAAW,CAAC,gBAAgB,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CACxB,CAAC;IAC1C,IAAI,UAAU,IAAI,UAAU,CAAC,aAAa,GAAG,CAAC,EAAE,CAAC;QAC/C,MAAM,CAAC,IAAI,GAAG,UAAU,CAAC,aAAa,CAAC;IACzC,CAAC;IAED,OAAO,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,EAAE;QAC3B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,KAAK,CAAC,IAAI,KAAK,wBAAwB;gBAAE,MAAM,CAAC,GAAG,GAAG,KAAK,CAAC,SAAS,CAAC;QAC5E,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,0BAA0B,EAAE,CAAC,OAAO,EAAE,EAAE;QAC9C,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACzC,IAAI,IAAI;YAAE,MAAM,CAAC,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,6EAA6E;IAC7E,uBAAuB;IACvB,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,OAAO,CAAC,cAAc,EAAE,CAAC,OAAO,EAAE,EAAE;QAClC,KAAK,MAAM,KAAK,IAAI,OAA6B,EAAE,CAAC;YAClD,IAAI,KAAK,CAAC,cAAc;gBAAE,SAAS;YACnC,IACE,SAAS;gBACT,KAAK,CAAC,SAAS,GAAG,OAAO,GAAG,KAAK;gBACjC,KAAK,CAAC,SAAS,GAAG,QAAQ,GAAG,KAAK,EAClC,CAAC;gBACD,SAAS,IAAI,KAAK,CAAC,KAAK,CAAC;YAC3B,CAAC;iBAAM,CAAC;gBACN,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC;gBACxB,QAAQ,GAAG,KAAK,CAAC,SAAS,CAAC;YAC7B,CAAC;YACD,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC;YAC1B,MAAM,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,EAAE,SAAS,GAAG,KAAK,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,sEAAsE;IACtE,2DAA2D;IAC3D,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,MAAM,cAAc,GAAG,IAAI,GAAG,EAAU,CAAC;IACzC,MAAM,YAAY,GAAG,CAAC,OAA2B,EAAE,EAAE;QACnD,KAAK,MAAM,KAAK,IAAI,OAA6B,EAAE,CAAC;YAClD,IAAI,CAAC,KAAK,CAAC,aAAa;gBAAE,SAAS;YACnC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;YACxC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACjC,CAAC;QACD,IAAI,CAAC,SAAS,CAAC,MAAM;YAAE,OAAO;QAC9B,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;QAC9C,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAClD,MAAM,KAAK,GACR,WAA6C,CAAC,gBAAgB;YAC/D,cAAc,CAAC,IAAI,CAAC;QACtB,MAAM,CAAC,GAAG;YACR,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;IACtE,CAAC,CAAC;IACF,OAAO,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,iBAAiB,EAAE,EAAE,EAAE,CAAC,CAAC;IAC1D,OAAO,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;IAErC,IAAI,IAAI,GAAG,KAAK,CAAC;IACjB,MAAM,IAAI,GAAG,GAAG,EAAE;QAChB,IAAI,IAAI;YAAE,OAAO;QACjB,KAAK,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,SAAS,EAAE,CAAC;YAC9C,OAAO,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;YAChC,QAAQ,CAAC,UAAU,EAAE,CAAC;QACxB,CAAC;QACD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAiB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;YACxE,OAAO,EAAE,GAAG,SAAS,IAAI,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,KAAK,EAAE;YAC3D,SAAS;YACT,SAAS;YACT,IAAI,EAAE,OAAO;YACb,IAAI;YACJ,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAE,CAAC;YAChC,IAAI;YACJ,SAAS;YACT,QAAQ,EAAE,KAAK;SAChB,CAAC,CAAC,CAAC;QACJ,IAAI,CAAC,MAAM,CAAC,MAAM;YAAE,OAAO;QAC3B,IAAI,GAAG,IAAI,CAAC;QACZ,gEAAgE;QAChE,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC;YACpB,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;SACtD,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,SAAS,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QACrD,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC;YACvC,KAAK,KAAK,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CACnE,GAAG,EAAE;gBACH,qEAAqE;YACvE,CAAC,CACF,CAAC;QACJ,CAAC;IACH,CAAC,CAAC;IACF,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,GAAG,EAAE;QACjD,IAAI,QAAQ,CAAC,eAAe,KAAK,QAAQ;YAAE,IAAI,EAAE,CAAC;IACpD,CAAC,CAAC,CAAC;IACH,gBAAgB,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;AACrC,CAAC,CAAC,EAAE,CAAC"}
@@ -1 +1 @@
1
- "use strict";(()=>{const e=document.currentScript,t=e?.dataset.site,r=e?.dataset.endpoint||e?.src.replace(/tracker\.js.*$/,"events");if(!t||!r)return;const s=()=>crypto.randomUUID?.()||`${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`;let n,o;try{n=sessionStorage._r||s(),sessionStorage._r=n,o=new URLSearchParams(location.search).get("ref")?.slice(0,64)||sessionStorage._a,o&&(sessionStorage._a=o)}catch{n=s(),o=new URLSearchParams(location.search).get("ref")?.slice(0,64)}const a=e.dataset.visitor||n;let i,c=0;const d=[],l=document.referrer?new URL(document.referrer).origin:void 0,p=e=>{const s=d.splice(0,20);if(!s.length)return;const n=new Blob([JSON.stringify({siteId:t,events:s})],{type:"application/json"});e&&navigator.sendBeacon(r,n)||fetch(r,{method:"POST",body:n,keepalive:1}).catch(()=>{}),d.length&&p(e)},u=(e,t,r)=>{const s=Date.now();d.push({eventId:`${n}.${s.toString(36)}.${c.toString(36)}`,sessionId:n,visitorId:a,type:e,name:t,path:location.pathname.slice(0,256)||"/",referrer:l,timestamp:s,sequence:c++,affiliateSlug:o,...r}),window.clearTimeout(i),i=window.setTimeout(p,1200)},h=()=>u("pageview");window.rastro=(e,t,r)=>{if("context"===e)return{sessionId:n,visitorId:a};"event"===e&&u("custom",`${t||"event"}`.slice(0,80),{properties:r})},document.addEventListener("click",e=>{const t=e.target?.closest("a,button,[data-rastro-event]");if(!t)return;const r=t.dataset.rastroEvent,s=t.href,n=s?new URL(s,location.href):void 0;u(r?"custom":n&&n.origin!==location.origin?"outbound":"click",r,{target:(t.dataset.rastroLabel||t.tagName).slice(0,80),href:n?.pathname.slice(0,256)})},{capture:1,passive:1});const g=e=>{const t=history[e];history[e]=function(e,r,s){t.call(history,e,r,s),queueMicrotask(h)}};g("pushState"),g("replaceState"),addEventListener("popstate",h),addEventListener("pagehide",()=>p(1)),setInterval(()=>{u("heartbeat"),p()},2e4),h()})();
1
+ (()=>{const e=document.currentScript,t=e?.dataset.site,s=e?.dataset.endpoint||e?.src&&new URL("events",e.src).href;if(!t||!s)return;const o=()=>crypto.randomUUID?.()||Math.random()+"",n=new URLSearchParams(location.search),r=n.get("ref")?.slice(0,64);let a,i,c;for(const e of["source","medium","campaign","term","content"]){const t=n.get("utm_"+e)?.slice(0,64);t&&((a??={})["utm_"+e]=t)}try{i=sessionStorage._r||o(),sessionStorage._r=i,c=r||sessionStorage._a,c&&(sessionStorage._a=c),a||=sessionStorage._u&&JSON.parse(sessionStorage._u),a&&(sessionStorage._u=JSON.stringify(a))}catch{i=o(),c=r}let d=e.dataset.visitor;try{d||=localStorage._rv||=o()}catch{}d||=i;let l,p=0;const g=[],h=document.referrer?new URL(document.referrer).origin:void 0,u=e=>{const o=g.splice(0,50);if(!o.length)return;const n=()=>new Blob([JSON.stringify({siteId:t,events:o,sentAt:Date.now()})]);let r=n();for(;r.size>64e3&&o.length>1;)g.unshift(o.pop()),r=n();e&&navigator.sendBeacon(s,r)||fetch(s,{method:"POST",body:r,keepalive:1}).catch(()=>{}),g.length&&u(e)},m=(e,t,s)=>{const o=Date.now();g.push({eventId:`${i}.${o.toString(36)}.${p}`,sessionId:i,visitorId:d,type:e,name:t,path:location.pathname.slice(0,256),referrer:h,timestamp:o,sequence:p++,affiliateSlug:c,...s}),clearTimeout(l),l=setTimeout(u,1200)};let v;const f=()=>{const e=location.pathname;e!==v&&(v=e,m("pageview",void 0,a&&{properties:a}))};window.rastro=(e,t,s)=>{if("context"===e)return{sessionId:i,visitorId:d};"event"===e&&m("custom",`${t||"event"}`.slice(0,80),s&&JSON.stringify(s).length>2e4?void 0:{properties:s})},document.addEventListener("click",e=>{const t=e.target?.closest("a,button,[data-rastro-event]");if(!t)return;const s=t.dataset.rastroEvent,o=t.href,n=o&&new URL(o),r=/^https?:$/.test(n?.protocol),a=r&&n.origin!==location.origin;m(s?"custom":a?"outbound":"click",s,{target:(t.dataset.rastroLabel||t.innerText||t.tagName).slice(0,64),href:r?((a?n.origin:"")+n.pathname).slice(0,256):void 0})},1);const S=e=>{const t=history[e];history[e]=(...e)=>{t.apply(history,e),f()}};S("pushState"),S("replaceState"),addEventListener("popstate",f),addEventListener("pagehide",()=>{m("leave"),u(1)}),addEventListener("pageshow",e=>{e.persisted&&(m("heartbeat"),u(1))}),setInterval(()=>{m("heartbeat"),u()},2e4),f()})();
@@ -0,0 +1 @@
1
+ (()=>{const t=document.currentScript,e=t?.dataset.site,n=t?.dataset.endpoint||t?.src&&new URL("events",t.src).href;if(!e||!n)return;const r=()=>crypto.randomUUID?.()||Math.random()+"";let a;try{a=sessionStorage._r||r(),sessionStorage._r=a}catch{a=r()}const s=t.dataset.visitor||a,o=location.pathname.slice(0,256),i={},c=[],d=(t,e,n)=>{try{const r=new PerformanceObserver(t=>e(t.getEntries()));r.observe({type:t,buffered:1,...n}),c.push({observer:r,handler:e})}catch{}},h=performance.getEntriesByType?.("navigation")[0];h&&h.responseStart>0&&(i.TTFB=h.responseStart),d("paint",t=>{for(const e of t)"first-contentful-paint"===e.name&&(i.FCP=e.startTime)}),d("largest-contentful-paint",t=>{const e=t[t.length-1];e&&(i.LCP=e.startTime)});let l=0,f=0,m=0;d("layout-shift",t=>{for(const e of t)e.hadRecentInput||(l&&e.startTime-m<1e3&&e.startTime-f<5e3?l+=e.value:(l=e.value,f=e.startTime),m=e.startTime,i.CLS=Math.max(i.CLS||0,1e3*l))});const u=[],p=new Set,v=t=>{for(const e of t)e.interactionId&&(p.add(e.interactionId),u.push(e.duration));if(!u.length)return;u.sort((t,e)=>e-t),u.length=Math.min(u.length,10);const e=performance.interactionCount||p.size;i.INP=u[Math.min(u.length-1,Math.floor(e/50))]};d("event",v,{durationThreshold:40}),d("first-input",v);let g=0;const y=()=>{if(g)return;for(const{observer:t,handler:e}of c)e(t.takeRecords()),t.disconnect();const t=Date.now(),r=Object.keys(i).map((e,n)=>({eventId:`${a}.${t.toString(36)}.v${n}`,sessionId:a,visitorId:s,type:"vital",name:e,value:Math.round(i[e]),path:o,timestamp:t,sequence:n}));if(!r.length)return;g=1;const d=new Blob([JSON.stringify({siteId:e,events:r,sentAt:t})]),h=navigator.sendBeacon?.bind(navigator);h&&h(n,d)||fetch(n,{method:"POST",body:d,keepalive:1}).catch(()=>{})};document.addEventListener("visibilitychange",()=>{"hidden"===document.visibilityState&&y()}),addEventListener("pagehide",y)})();
@@ -0,0 +1,171 @@
1
+ # Realistic ingest validation, 2026-08-20
2
+
3
+ This report preserves the performance evidence produced while developing PR
4
+ [#8](https://github.com/amageweb/amage-rastro/pull/8), merged as `93a0b4e`. It
5
+ is a historical engineering record, not a production SLA.
6
+
7
+ ## Status
8
+
9
+ - The realistic workload demonstrated the 100 million accepted-events/day target
10
+ during a sustained ten-minute soak.
11
+ - The sustained run predates the current `--certify` contract and did not
12
+ preserve a run-scoped JSON artifact with aggregate reconciliation.
13
+ - A later smoke against the final PR code reconciled every expected stored
14
+ event, but lasted only 30 seconds.
15
+ - This historical gap was closed by the four-profile runs recorded in
16
+ [`2026-08-21-formal-certification.md`](2026-08-21-formal-certification.md).
17
+ Lean and realistic exceeded 100 million stored events/day; heavy and feature
18
+ completed cleanly below that target. Trusted conversions remain pending.
19
+
20
+ Do not collapse these statements into a generic claim that every workload is
21
+ certified for 100 million stored events/day.
22
+
23
+ ## Environment
24
+
25
+ | Item | Value |
26
+ | --------------- | ----------------------------------------------------------------- |
27
+ | Date | 2026-08-20 |
28
+ | Platform | Synapse-managed Convex |
29
+ | Deployment | DEV canary `fast-finch-7454` |
30
+ | Allocation | 4 GiB |
31
+ | Site scope | One dedicated benchmark site |
32
+ | Profile | `realistic` |
33
+ | Concurrency | 75 workers |
34
+ | Batch size | 50 events |
35
+ | Session pool | 2,048 reused sessions |
36
+ | Browser context | 12 geographic locations and 4 user-agent families |
37
+ | Event mix | Pageviews, clicks, custom events, outbound events, and heartbeats |
38
+
39
+ The deployment name is historical and may be deleted or repointed. Never use
40
+ this document as deployment configuration.
41
+
42
+ ## Sustained result
43
+
44
+ The sustained soak ran after the raw-event batching and five-second live-sweep
45
+ work in `bbfb9ad`, before the final review fixes later included in `93a0b4e`.
46
+
47
+ | Metric | Result |
48
+ | ---------------------------- | ------------------------: |
49
+ | Target duration | 600 seconds |
50
+ | Accepted events | 1,067,650 |
51
+ | Failed requests | 0 |
52
+ | Rejected events | 0 |
53
+ | Accepted throughput | 1,773 events/s |
54
+ | Accepted daily-rate estimate | 153.2 million events/day |
55
+ | Target daily rate | 100 million events/day |
56
+ | Headroom over target | 53.2% |
57
+ | Request latency p99 | approximately 5.4 seconds |
58
+
59
+ The target requires approximately 1,157 accepted events/s. The daily figure is
60
+ an extrapolation from the measured soak, not a full-day load test.
61
+
62
+ This run established sustained accepted-ingress capacity. Its exact heartbeat
63
+ count and stored-event delta were not retained, so it must not be presented as a
64
+ formally reconciled stored-events certification under the current harness.
65
+
66
+ ## Reconciled corroboration
67
+
68
+ A separate two-minute realistic rerun on the same canary used the same
69
+ concurrency, batch size, and session pool:
70
+
71
+ | Metric | Result |
72
+ | ---------------------------- | ---------------------: |
73
+ | Run label | `realistic-2m-rerun` |
74
+ | Run ID | `mt1ydsq5-jjmbjv` |
75
+ | Target duration | 120 seconds |
76
+ | Measured elapsed time | 122.021 seconds |
77
+ | Requests completed | 4,363 |
78
+ | Accepted events | 218,150 |
79
+ | Accepted heartbeats | 10,775 |
80
+ | Expected stored events | 207,375 |
81
+ | Aggregate event delta | 207,375 |
82
+ | Reconciliation | exact |
83
+ | Failed requests | 0 |
84
+ | Duplicate events | 0 |
85
+ | Rejected events | 0 |
86
+ | Accepted throughput | 1,788 events/s |
87
+ | Accepted daily-rate estimate | 154,466,612 events/day |
88
+ | Request latency p50 | 2,049 ms |
89
+ | Request latency p95 | 2,570 ms |
90
+ | Request latency p99 | 3,082 ms |
91
+ | Request latency max | 3,366 ms |
92
+
93
+ Reconciliation was a site-wide before/after delta. It is reliable only because
94
+ the benchmark site was kept idle apart from this run.
95
+
96
+ ## Final-code smoke
97
+
98
+ After the final correctness review, commit `c43d971` was deployed to the canary.
99
+ That source was subsequently squash-merged unchanged as `93a0b4e`. A 30-second
100
+ smoke produced:
101
+
102
+ | Metric | Result |
103
+ | ---------------------------- | ---------------------: |
104
+ | Run label | `final-pr-smoke` |
105
+ | Run ID | `mt2072x6-4c0avl` |
106
+ | Measured elapsed time | 31.747 seconds |
107
+ | Requests completed | 1,243 |
108
+ | Accepted events | 62,150 |
109
+ | Accepted heartbeats | 2,486 |
110
+ | Expected stored events | 59,664 |
111
+ | Aggregate event delta | 59,664 |
112
+ | Reconciliation | exact |
113
+ | Failed requests | 0 |
114
+ | Duplicate events | 0 |
115
+ | Rejected events | 0 |
116
+ | Accepted throughput | 1,958 events/s |
117
+ | Stored throughput | 1,879 events/s |
118
+ | Accepted daily-rate estimate | 169,139,895 events/day |
119
+ | Stored daily-rate estimate | 162,374,299 events/day |
120
+ | Request latency p50 | 1,882 ms |
121
+ | Request latency p95 | 2,430 ms |
122
+ | Request latency p99 | 2,733 ms |
123
+ | Request latency max | 2,827 ms |
124
+
125
+ The harness correctly marked this result as an estimate, not a certification,
126
+ because it was shorter than 600 seconds.
127
+
128
+ ## Reproduction
129
+
130
+ Run from a repository checkout against a confirmed non-production deployment.
131
+ Use a newly created or otherwise idle benchmark site, and preserve stdout as a
132
+ JSON artifact in the pull request or release evidence.
133
+
134
+ ```bash
135
+ npm run --silent benchmark:ingest -- \
136
+ --url https://your-deployment.site.example/rastro/events \
137
+ --convex-url https://your-deployment.example \
138
+ --site-id your_dedicated_benchmark_site \
139
+ --origin https://your-allowed-origin.example \
140
+ --profile realistic \
141
+ --duration 600 \
142
+ --concurrency 75 \
143
+ --batch-size 50 \
144
+ --certify
145
+ ```
146
+
147
+ A passing harness certification must include:
148
+
149
+ - `terminationReason: "duration"` and `durationCompleted: true`;
150
+ - `certification.requested: true` and `certification.passed: true`;
151
+ - zero request failures, duplicate events, rejected events, and failed events;
152
+ - completed request accounting;
153
+ - reliable and exact run-session reconciliation;
154
+ - separate accepted and stored throughput/capacity fields.
155
+
156
+ Release evidence should additionally record deployment health and successful
157
+ dashboard QA. Those checks are external to the harness and do not determine
158
+ `certification.passed`.
159
+
160
+ ## Limitations
161
+
162
+ - Traffic was synthetic and generated from one client process.
163
+ - The workload covered one site and a fixed 2,048-session pool.
164
+ - The sustained evidence measured accepted ingress, not storage cost or a
165
+ complete production-day traffic distribution.
166
+ - No database cost, network egress cost, multi-region behavior, failover, or
167
+ noisy-neighbor behavior was certified.
168
+ - The Live Atlas intentionally renders a bounded sample rather than every live
169
+ session as an individual DOM marker.
170
+ - `heavy`, `feature`, and trusted server-side conversion profiles require
171
+ separate results; their capacity must not be inferred from this report.
@@ -0,0 +1,353 @@
1
+ # Formal ingest certification, 2026-08-21
2
+
3
+ This report preserves four isolated ten-minute benchmark runs against the
4
+ non-production Synapse canary. It is engineering evidence for the tested commit
5
+ and deployment, not a production SLA.
6
+
7
+ ## Environment
8
+
9
+ | Item | Value |
10
+ | ------------------- | -------------------------------------------------- |
11
+ | Git commit | `e6ae92658c86c1931e697fa2a38c80fd902f7afa` |
12
+ | Package version | `0.1.0-alpha.3` |
13
+ | Deployment | DEV `fast-finch-7454` |
14
+ | Benchmark site | `kx75pnr9wap522n0zmh4nnb4jh8cv110` |
15
+ | Driver | One local Node.js process |
16
+ | Reconciliation | Exact run-scoped session IDs, indexed pages of 100 |
17
+ | Health before/after | `ok: true` |
18
+ | GeoIP provider | Disabled |
19
+
20
+ All runs used `--duration 600 --certify`. Runs were sequential so the profiles
21
+ did not compete with each other. A harness certification means the run lasted
22
+ the required duration, had clean request accounting, and reconciled every
23
+ expected stored event. Meeting the separate 100 million stored-events/day target
24
+ requires at least 1,157.41 stored events/second.
25
+
26
+ ## Results
27
+
28
+ | Profile | Concurrency | Batch | Accepted | Stored | Stored/s | Certified stored/day | p95 | Exact reconciliation | 100M/day target |
29
+ | --------- | ----------: | ----: | -------: | ------: | -------: | -------------------: | -------: | -------------------- | ---------------- |
30
+ | Lean | 100 | 50 | 952,600 | 952,600 | 1,582 | 136,654,617 | 4,543 ms | 952,600 / 952,600 | Pass |
31
+ | Realistic | 75 | 50 | 885,700 | 842,080 | 1,398 | 120,808,947 | 4,247 ms | 842,080 / 842,080 | Pass |
32
+ | Heavy | 100 | 20 | 353,980 | 353,980 | 588 | 50,769,480 | 5,787 ms | 353,980 / 353,980 | **Below target** |
33
+ | Feature | 100 | 15 | 232,170 | 232,170 | 384 | 33,218,356 | 6,226 ms | 232,170 / 232,170 | **Below target** |
34
+
35
+ The realistic run accepted 43,620 heartbeats, which intentionally update live
36
+ presence without increasing persistent event counts. Its accepted-versus-stored
37
+ difference is therefore exact. The heavy request measured 62,054 bytes and
38
+ sustained 1,828,443 request bytes/second below the 64 KiB HTTP body limit.
39
+
40
+ ## Heavy recertification after byte-accounting optimization
41
+
42
+ Commit `4460ad34314a6e15c24a0c78aab6e6ce85db5c60` reused each event's exact
43
+ UTF-8 byte measurement for both batch validation and rate limits, removing one
44
+ full-payload serialization from the ingestion transaction. A new isolated
45
+ ten-minute run on 2026-08-22 used the same deployment, site, concurrency, batch
46
+ size, and heavy payload.
47
+
48
+ | Result | Original | Recertified | Change |
49
+ | -------------------- | ---------: | ----------: | -----: |
50
+ | Stored events/s | 588 | 887 | +50.9% |
51
+ | Certified stored/day | 50,769,480 | 76,623,702 | +50.9% |
52
+ | Request bytes/s | 1,828,443 | 2,759,875 | +50.9% |
53
+ | p95 latency | 5,787 ms | 3,670 ms | -36.6% |
54
+
55
+ The recertification remained below the separate 100 million events/day target,
56
+ but raised the measured heavy-payload ceiling without changing payload shape or
57
+ weakening validation, rate limits, or reconciliation.
58
+
59
+ ```json
60
+ {
61
+ "label": "heavy-2026-08-22T04:03:22.517Z",
62
+ "runId": "f3943151-bc71-4806-b609-69faf8bdfb0c",
63
+ "profile": "heavy",
64
+ "targetDurationSeconds": 600,
65
+ "elapsedMs": 601726,
66
+ "terminationReason": "duration",
67
+ "durationCompleted": true,
68
+ "certification": {
69
+ "requested": true,
70
+ "passed": true,
71
+ "minimumDurationSeconds": 600
72
+ },
73
+ "concurrency": 100,
74
+ "batchSize": 20,
75
+ "sessionPool": 2048,
76
+ "maximumRequests": null,
77
+ "requests": { "started": 26682, "completed": 26682, "failures": 0 },
78
+ "events": {
79
+ "sent": 533640,
80
+ "heartbeats": 0,
81
+ "accepted": 533640,
82
+ "stored": 533640,
83
+ "duplicates": 0,
84
+ "rejected": 0,
85
+ "failed": 0
86
+ },
87
+ "errors": {},
88
+ "throughput": {
89
+ "eventsPerSecond": 887,
90
+ "storedEventsPerSecond": 887,
91
+ "estimatedEventsPerDay": 76623702,
92
+ "estimatedStoredEventsPerDay": 76623702,
93
+ "certifiedEventsPerDay": 76623702,
94
+ "certifiedStoredEventsPerDay": 76623702,
95
+ "requestBytesPerSecond": 2759875
96
+ },
97
+ "latencyMs": { "p50": 2035, "p95": 3670, "p99": 4755, "max": 8139 },
98
+ "reconciliation": {
99
+ "scope": "run-session-ids",
100
+ "reliable": true,
101
+ "expectedSessions": 2048,
102
+ "requestedSessions": 2048,
103
+ "foundSessions": 2048,
104
+ "acceptedEvents": 533640,
105
+ "acceptedHeartbeats": 0,
106
+ "expectedStoredEvents": 533640,
107
+ "storedEvents": 533640,
108
+ "matches": true
109
+ }
110
+ }
111
+ ```
112
+
113
+ ## Run artifacts
114
+
115
+ ### Lean
116
+
117
+ ```json
118
+ {
119
+ "label": "lean-2026-08-21T13:31:27.434Z",
120
+ "runId": "41788de1-964e-4315-8b00-f295e202f2e0",
121
+ "profile": "lean",
122
+ "targetDurationSeconds": 600,
123
+ "elapsedMs": 602282,
124
+ "terminationReason": "duration",
125
+ "durationCompleted": true,
126
+ "certification": {
127
+ "requested": true,
128
+ "passed": true,
129
+ "minimumDurationSeconds": 600
130
+ },
131
+ "concurrency": 100,
132
+ "batchSize": 50,
133
+ "sessionPool": "unique-per-request",
134
+ "maximumRequests": null,
135
+ "requests": { "started": 19052, "completed": 19052, "failures": 0 },
136
+ "events": {
137
+ "sent": 952600,
138
+ "heartbeats": 0,
139
+ "accepted": 952600,
140
+ "stored": 952600,
141
+ "duplicates": 0,
142
+ "rejected": 0,
143
+ "failed": 0
144
+ },
145
+ "errors": {},
146
+ "throughput": {
147
+ "eventsPerSecond": 1582,
148
+ "storedEventsPerSecond": 1582,
149
+ "estimatedEventsPerDay": 136654617,
150
+ "estimatedStoredEventsPerDay": 136654617,
151
+ "certifiedEventsPerDay": 136654617,
152
+ "certifiedStoredEventsPerDay": 136654617,
153
+ "requestBytesPerSecond": 438830
154
+ },
155
+ "latencyMs": { "p50": 2979, "p95": 4543, "p99": 5217, "max": 5628 },
156
+ "reconciliation": {
157
+ "scope": "run-session-ids",
158
+ "reliable": true,
159
+ "expectedSessions": 19052,
160
+ "requestedSessions": 19052,
161
+ "foundSessions": 19052,
162
+ "acceptedEvents": 952600,
163
+ "acceptedHeartbeats": 0,
164
+ "expectedStoredEvents": 952600,
165
+ "storedEvents": 952600,
166
+ "matches": true
167
+ }
168
+ }
169
+ ```
170
+
171
+ ### Realistic
172
+
173
+ ```json
174
+ {
175
+ "label": "realistic-2026-08-21T13:42:07.118Z",
176
+ "runId": "8a883d9a-537a-4ce5-b300-be77995ba5da",
177
+ "profile": "realistic",
178
+ "targetDurationSeconds": 600,
179
+ "elapsedMs": 602238,
180
+ "terminationReason": "duration",
181
+ "durationCompleted": true,
182
+ "certification": {
183
+ "requested": true,
184
+ "passed": true,
185
+ "minimumDurationSeconds": 600
186
+ },
187
+ "concurrency": 75,
188
+ "batchSize": 50,
189
+ "sessionPool": 2048,
190
+ "maximumRequests": null,
191
+ "requests": { "started": 17714, "completed": 17714, "failures": 0 },
192
+ "events": {
193
+ "sent": 885700,
194
+ "heartbeats": 43620,
195
+ "accepted": 885700,
196
+ "stored": 842080,
197
+ "duplicates": 0,
198
+ "rejected": 0,
199
+ "failed": 0
200
+ },
201
+ "errors": {},
202
+ "throughput": {
203
+ "eventsPerSecond": 1471,
204
+ "storedEventsPerSecond": 1398,
205
+ "estimatedEventsPerDay": 127066887,
206
+ "estimatedStoredEventsPerDay": 120808947,
207
+ "certifiedEventsPerDay": 127066887,
208
+ "certifiedStoredEventsPerDay": 120808947,
209
+ "requestBytesPerSecond": 445267
210
+ },
211
+ "latencyMs": { "p50": 2257, "p95": 4247, "p99": 5122, "max": 6133 },
212
+ "reconciliation": {
213
+ "scope": "run-session-ids",
214
+ "reliable": true,
215
+ "expectedSessions": 2048,
216
+ "requestedSessions": 2048,
217
+ "foundSessions": 2048,
218
+ "acceptedEvents": 885700,
219
+ "acceptedHeartbeats": 43620,
220
+ "expectedStoredEvents": 842080,
221
+ "storedEvents": 842080,
222
+ "matches": true
223
+ }
224
+ }
225
+ ```
226
+
227
+ ### Heavy
228
+
229
+ ```json
230
+ {
231
+ "label": "heavy-2026-08-21T13:52:43.382Z",
232
+ "runId": "8cd7221e-2e33-4e94-9f9d-13bb4a84c60e",
233
+ "profile": "heavy",
234
+ "targetDurationSeconds": 600,
235
+ "elapsedMs": 602407,
236
+ "terminationReason": "duration",
237
+ "durationCompleted": true,
238
+ "certification": {
239
+ "requested": true,
240
+ "passed": true,
241
+ "minimumDurationSeconds": 600
242
+ },
243
+ "concurrency": 100,
244
+ "batchSize": 20,
245
+ "sessionPool": 2048,
246
+ "maximumRequests": null,
247
+ "requests": { "started": 17699, "completed": 17699, "failures": 0 },
248
+ "events": {
249
+ "sent": 353980,
250
+ "heartbeats": 0,
251
+ "accepted": 353980,
252
+ "stored": 353980,
253
+ "duplicates": 0,
254
+ "rejected": 0,
255
+ "failed": 0
256
+ },
257
+ "errors": {},
258
+ "throughput": {
259
+ "eventsPerSecond": 588,
260
+ "storedEventsPerSecond": 588,
261
+ "estimatedEventsPerDay": 50769480,
262
+ "estimatedStoredEventsPerDay": 50769480,
263
+ "certifiedEventsPerDay": 50769480,
264
+ "certifiedStoredEventsPerDay": 50769480,
265
+ "requestBytesPerSecond": 1828443
266
+ },
267
+ "latencyMs": { "p50": 2968, "p95": 5787, "p99": 6942, "max": 7839 },
268
+ "reconciliation": {
269
+ "scope": "run-session-ids",
270
+ "reliable": true,
271
+ "expectedSessions": 2048,
272
+ "requestedSessions": 2048,
273
+ "foundSessions": 2048,
274
+ "acceptedEvents": 353980,
275
+ "acceptedHeartbeats": 0,
276
+ "expectedStoredEvents": 353980,
277
+ "storedEvents": 353980,
278
+ "matches": true
279
+ }
280
+ }
281
+ ```
282
+
283
+ ### Feature
284
+
285
+ ```json
286
+ {
287
+ "label": "feature-2026-08-21T14:03:20.654Z",
288
+ "runId": "8e60bc5f-6fff-4791-b02c-0e7340767b55",
289
+ "profile": "feature",
290
+ "targetDurationSeconds": 600,
291
+ "elapsedMs": 603868,
292
+ "terminationReason": "duration",
293
+ "durationCompleted": true,
294
+ "certification": {
295
+ "requested": true,
296
+ "passed": true,
297
+ "minimumDurationSeconds": 600
298
+ },
299
+ "concurrency": 100,
300
+ "batchSize": 15,
301
+ "sessionPool": 2048,
302
+ "maximumRequests": null,
303
+ "requests": { "started": 15478, "completed": 15478, "failures": 0 },
304
+ "events": {
305
+ "sent": 232170,
306
+ "heartbeats": 0,
307
+ "accepted": 232170,
308
+ "stored": 232170,
309
+ "duplicates": 0,
310
+ "rejected": 0,
311
+ "failed": 0
312
+ },
313
+ "errors": {},
314
+ "throughput": {
315
+ "eventsPerSecond": 384,
316
+ "storedEventsPerSecond": 384,
317
+ "estimatedEventsPerDay": 33218356,
318
+ "estimatedStoredEventsPerDay": 33218356,
319
+ "certifiedEventsPerDay": 33218356,
320
+ "certifiedStoredEventsPerDay": 33218356,
321
+ "requestBytesPerSecond": 119846
322
+ },
323
+ "latencyMs": { "p50": 3523, "p95": 6226, "p99": 7722, "max": 8524 },
324
+ "reconciliation": {
325
+ "scope": "run-session-ids",
326
+ "reliable": true,
327
+ "expectedSessions": 2048,
328
+ "requestedSessions": 2048,
329
+ "foundSessions": 2048,
330
+ "acceptedEvents": 232170,
331
+ "acceptedHeartbeats": 0,
332
+ "expectedStoredEvents": 232170,
333
+ "storedEvents": 232170,
334
+ "matches": true
335
+ }
336
+ }
337
+ ```
338
+
339
+ ## Conclusions
340
+
341
+ - Lean and realistic traffic exceeded 100 million stored events/day for a full
342
+ ten minutes with exact reconciliation and no failures.
343
+ - Heavy and feature traffic completed cleanly but did not meet the 100 million
344
+ stored-events/day target at the tested payloads and concurrency. The latest
345
+ heavy ceiling is 76.62 million events/day after recertification.
346
+ - The updated heavy result demonstrates a different ceiling: 2.76 MB/s of
347
+ near-limit requests. It must not be generalized to minimal or realistic
348
+ payloads.
349
+ - Trusted financial conversions were not exercised. They require a separate
350
+ server-side driver because browser traffic cannot cross the financial trust
351
+ boundary.
352
+ - GeoIP provider behavior was not benchmarked because provider enrichment was
353
+ disabled on this deployment.