Pixel Ingress Perimeter — rule-04 decision package
Status: built, flag-off everywhere; the activation decision is OPEN and
is the owner's (GATE 2). Nothing in the build this document ships changes an
IAM binding, flips a flag, or serves a byte of pixel traffic.
Claim label: built, pre-benchmark — this path has never carried a live
batch.
Lane: ORACLE pre-conversion follow-on (pixel ingress perimeter package,
2026-08-19); plan of record docs/ORACLE_PRECONVERSION_INTENT_v1.1.md.
1. The problem this closes
The ORACLE build landed the extender's /pixel/events door IAM-locked with no
application-level auth — correct under rule 04 (the single-ingress collapse
removed the extender from the intended-public register, and a second public
receiver would re-open the door that collapse closed). But browsers cannot
mint Cloud Run OIDC, so as landed there was no path from a consented
storefront browser to the door at all: activating the pixel would have
pointed every batch at a 403 wall, and the tempting quick fix — a public
invoker binding on the extender — is exactly the rule-04 hazard.
2. Topology (as built)
storefront browser (Shopify Web Pixel sandbox, consent-gated capture-core)
│ POST /pixel/collect — batch body carries the per-install ingest token
▼
service-marketing-connectors ← THE public boundary (existing, load-bearing
│ verify token (constant-time) `allUsers` binding; NO new binding anywhere)
│ tenant from registry row flag PIXEL_COLLECT_ENABLED, default OFF ⇒ 404
▼ OIDC (outbound_headers)
intent-shopify-extender /pixel/events ← stays IAM-locked, code unchanged
▼
Cell 33 /v1/signals ← the governed door: consent fail-closed,
taxonomy, O-1 bright lines re-checked
The browser-facing edge lives on the service that is already public by design (webhook HMAC + OAuth — rule 04's load-bearing binding), preserving the single-ingress invariant: one public Shopify boundary, downstream consumers reached only over OIDC.
3. Auth design
- Per-install body token.
sendBeaconcannot set headers, so the credential rides in the batch body:ingest_token = HMAC-SHA256(SHOPIFY_PIXEL_INGEST_SECRET, "pixel:" + install_id). One derivation function (pixel_ingest_token) both ships the token (B5 activation hook → pixel settings) and verifies it (/pixel/collect), constant-time, stateless at the door. - Rotation rides the install lifecycle. Uninstall→reinstall mints a new
install_id(§4.4 of the OAuth design), so a stale token dies with the install that issued it. Rotating the secret rotates every shop at once; re-run pixel activation afterwards to ship fresh settings. - No install-status oracle. Unknown shop, uninstalled shop, and wrong token are all the same uniform 401; internal metrics split the causes.
- Fail closed, loudly. Secret unset ⇒ every batch answers 503
not_configured(never a fake 401 that sends an operator debugging tokens), and the activation hook defers rather than activate a pixel that could not authenticate. Registry outage ⇒ 503, never a guessed tenant. - Tenant is never client-chosen. The forwarded batch carries the registry
row's
tenant_id; whatever the browser sent is ignored. The token never leaves the gateway. - Blast radius of a leaked token: forged behavioral signals for that one shop — a write-only door into Cell 33's own gate battery (consent fail-closed, closed taxonomy, O-1/audio/gaze/geo bright lines, closed behavioral contract). No read surface exists; no person data can be extracted through this door. Batch caps (500 events / 1 MiB) bound volume; Cloud Run edge throttling is the DoS backstop.
4. What is public, exactly
| Surface | Unauthenticated answer | Why |
|---|---|---|
gateway /pixel/collect, flag OFF (today) |
404 both verbs | route behaves as absent; public surface byte-identical to pre-build |
gateway /pixel/collect, flag ON |
401 without a valid token (503 if misconfigured); CORS preflight 204 | the decided public posture; every response body is counts/reason only |
extender /pixel/events |
403 at the IAM layer (unchanged) | never public, before or after |
/health and /readyz stay liveness/readiness-only (issue #676); pixel
posture booleans live on the verify_caller-gated /api/v1/providers.
5. GATE-2 activation order (the open decision)
Each step is safe alone; the order makes every intermediate state fail-closed. Do not reorder — in particular the flag is LAST, so no public 503/401 surface exists before the lane behind it is real.
- Bind
SHOPIFY_PIXEL_INGEST_SECRET(Secret Manager) on the gateway. Value out-of-band, never in chat (operator register item 6 discipline). - Set
SHOPIFY_PIXEL_COLLECT_URL= the gateway's own public origin. - Confirm
INTENT_EXTENDER_URLstill set (webhook fan-out already uses it). - Extender: set
PIXEL_EVENTS_ENABLED=1(route exists, still IAM-only). - Gateway: set
PIXEL_COLLECT_ENABLED=1— the door opens. - Re-run pixel activation for installed shops (install is re-runnable) so
settings ship
ingest_url+install_id+ingest_token+ O-2 knobs (vdi_threshold,dwell_threshold_ms). - Smoke, in this order: bad token ⇒ 401; no token ⇒ 401; valid token ⇒ 200
with forwarded counts; extender
/healthshowspixel_events_enabled: true; gateway/api/v1/providersshows both pixel booleans true. (Pre-flip, assert/pixel/collect⇒ 404 — the flag-off state is the baseline every smoke starts from.)
Prerequisites that remain owner/operator-held and are not part of this package: the first holdout registration before any activation flag that exposes users (constitution — holdout precedes activation), Shopify app project pixel artifact deployment (OP row), and the label stream.
6. Explicitly out of scope for this build
No IAM change (no binding added or removed anywhere). No flag flips. No BQ
DDL. No holdout registration, no dashboard surfacing, no causal-credit
change (master prompt out-of-scope list). The superseded twin design
(6c17727, token verify on the extender itself) was deliberately NOT ported
as-is: the landed topology puts the browser edge on the gateway, and the
extender keeps zero secret-reading code — the same posture as the webhook
HMAC boundary ("the HMAC boundary and the shop pin live at the gateway").
7. Enforcement, both directions
tests/connectors/test_pixel_collect.py pins: flag-off 404 (source-literal
OFF default asserted), secret-unset 503, uniform 401s (bad token / unknown
shop / missing fields — bodies byte-identical), constant-time compare in
source, valid-token forward (tenant from row, token stripped, OIDC headers),
caps, CORS, and the hook's defer/ship behavior including derivation
cross-assertion against the door's verifier. PIXEL_SETTINGS_ALLOWED_KEYS
keeps its refusal test (person-shaped keys never ship) and gains the named
stream. capture-core.test.mjs pins the fail-closed configuredSender and
the batch auth fields.