Beacon — spec

Table of Contents

1. Purpose

Beacon is the shared envelope. Two callers — the passive tracker and the explicit-page-view module window._beacon.pageView() — build the same JSON via wal-sh.adtech.beacon.core/build. The downstream server sees one schema; the browser sees two callers.

The envelope predates the tracker; it was designed for navigator.sendBeacon to a sinkhole and later reused for the pixel transport (MDN Web Docs, n.d.-b), (MDN Web Docs, n.d.-a). Keeping a single builder + a single JSON schema is the contract: any module fires events, downstream cannot tell them apart at the payload level.

ORACLE: src/wal_sh/adtech/beacon/core.cljc. Endpoint OpenAPI: docs/beacon-termbox-spec.json.

2. Requirements

B1
One builder — beacon.core/build — produces (payload, json, oversize?) for both callers. No parallel path.
B2
Payload keys are a fixed base set (event, ts, session, url, referrer, userAgent, screen, timezone, pageID) plus a caller-supplied :extra merged last.
B3
An empty or blank event-name returns nil from build — the browser adapter short-circuits without firing.
B4
payload->json hand-rolls JSON so core.cljc stays dependency-free. Values are strings or numbers only; booleans are emitted as bare true=/=false. Escape sequences are quote, backslash, and control chars.
B5
oversize? is the byte length of the UTF-8 JSON compared against max-payload-bytes (65,536, the sendBeacon cap). The browser adapter may still choose to send oversized events via fetch keepalive — the flag advertises the concern, not the response.
B6
Session id is caller-supplied; gen-session-id takes a numeric seed and produces s-<base36> (JVM: Long/toString; JS: .toString(36) on absolute value).
B7
Endpoint URL is a literal in core (beacon.termbox.org/webhook historically; the tracker's pixel path is a separate constant in tracker.core). Downstream retargeting is a constant swap, not a code change.

3. Contract signature

Pure (core.cljc):

(gen-session-id seed)                             ; -> "s-<base36>"
(valid-event? event-name)                         ; -> bool
(payload event-name ctx)                          ; -> map or nil
(payload->json m)                                 ; -> string or nil
(oversize? json-str)                              ; -> bool
(build event-name ctx)
;; -> {:payload <map> :json <string> :oversize? <bool>} or nil

Browser surface:

window._beacon = {
  pageView:      (fn [] -> nil)                   ; fires "pageview"
  send:          (fn [event extra] -> nil)        ; low-level, sendBeacon
  session:       (fn [] -> string)                ; opaque session id
}

Endpoint contract (docs/beacon-termbox-spec.json, abridged):

  • POST /webhook — JSON body ≤ 64 KiB; 204 No Content on success.
  • GET /pixel?<params> — returns a 1×1 transparent GIF, logs all params. Used by the tracker under CSP.
  • GET /spec.json — OpenAPI 3.0 self-description.

4. Payload shape (verbatim)

{
  "event":     "sponsored.impression",
  "ts":        1697000000000,
  "session":   "s-abc123",
  "url":       "https://wal.sh/search?q=dafny",
  "referrer":  "",
  "userAgent": "...",
  "screen":    "1920x1080",
  "timezone":  "America/New_York",
  "pageID":    "",
  "elId":      "sponsored-sr-001",
  "elKind":    "sponsored-product",
  "creativeId":"sr-001",
  "unit":      "product",
  "position":  3
}

Extras (elId through position above, plus sponsor, pageIdx, poolIdx, isDup, dwellMs, depthPct, …) come from the tracker's element-payload merge before query-encoding.

5. Related literature

  • sendBeacon specification and its keepalive counterpart: MDN (MDN Web Docs, n.d.-b). sendBeacon is the intent-preserving choice for events fired at page-unload; the envelope was sized around its 64 KiB cap.
  • Image() constructor pixel transport, still the industry-standard fallback: MDN (MDN Web Docs, n.d.-a).
  • The tracking-envelope shape (event + session + URL + user-agent extras) matches the profile documented for third-party trackers in Roesner et al.'s NSDI 2012 measurement (Roesner, Kohno, and Wetherall 2012) and Englehardt & Narayanan's CCS 2016 million-site crawl (Englehardt and Narayanan 2016).
  • CEDDL (pageID) is the customer-experience data-layer standard the pageID field is named after; also spelled pageInfo.pageID in the W3C CEDDL 1.0 spec. Modelled but not fully wired.
  • Sec-Fetch-* headers give the origin-side signal that would let a server-side pipeline distinguish beacon from real navigation (W3C 2021); the current sinkhole doesn't use them.
  • OpenRTB defines a comparable envelope for bid-request payloads; our envelope is smaller and always outbound (IAB Tech Lab 2016).

6. Cross-references

  • tracker/spec.org — primary caller; reuses build verbatim for every event.
  • attribution-audit/spec.org — the ledger appends touchpoint records that are shaped like beacon extras (source, medium, campaign).
  • ORACLE:
    • src/wal_sh/adtech/beacon/core.cljc (pure)
    • docs/beacon-termbox-spec.json (endpoint OpenAPI)
  • [BROKEN LINK: No match for fuzzy expression: *2007–2013: Programmatic exchange era] for the RTB envelope ancestry.
  • The Four-Boundary Spec Mapping — OpenAPI as the message-boundary spec technology this envelope sits in.
  • Web Trackers Analysis (Alexa Top 800) — the 2012 pixel-transport ecosystem the beacon module inherits from.

7. Open questions

  • Retry semantics. Neither the pixel nor the sendBeacon call retries on network failure; the browser doesn't guarantee delivery. For research the loss rate is acceptable; a production system would add a per-session write-ahead buffer in localStorage flushed on next pageview. The trade-off is that persistence turns the tracker into a first-party persistent identifier — precisely the pattern Acar et al. document as evercookie substrate (Acar et al. 2014).
  • Response echoing. The pixel is a 1×1 GIF with no envelope response. An X-Server-Time header would let us measure clock skew per session but adds latency.
Acar, Gunes, Christian Eubank, Steven Englehardt, Marc Juarez, Arvind Narayanan, and Claudia Diaz. 2014. “The Web Never Forgets: Persistent Tracking Mechanisms in the Wild.” In Proceedings of the 2014 Acm Sigsac Conference on Computer and Communications Security (Ccs ’14), 674–89. https://doi.org/10.1145/2660267.2660347.
Englehardt, Steven, and Arvind Narayanan. 2016. “Online Tracking: A 1-Million-Site Measurement and Analysis.” In Proceedings of the 2016 Acm Sigsac Conference on Computer and Communications Security (Ccs ’16), 1388–1401. https://doi.org/10.1145/2976749.2978313.
IAB Tech Lab. 2016. “Openrtb Specification.” https://iabtechlab.com/standards/openrtb/.
MDN Web Docs. n.d.-a. “Image() Constructor.” https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/Image.
———. n.d.-b. “Navigator: Sendbeacon() Method.” https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon.
Roesner, Franziska, Tadayoshi Kohno, and David Wetherall. 2012. “Detecting and Defending against Third-Party Tracking on the Web.” In 9Th Usenix Symposium on Networked Systems Design and Implementation (Nsdi 12), 155–68. https://www.usenix.org/conference/nsdi12/technical-sessions/presentation/roesner.
W3C. 2021. “Fetch Metadata Request Headers.” https://www.w3.org/TR/fetch-metadata/.