carlos / platform Public

edge: carlos.js vendored, a revalidating loader and an immutable hashed asset

internal/edge/carlosjs.go: the browser snippet (spec §3.4) vendored as a Go
string, never a CDN. carlosJSHash is 12 hex chars of sha256(carlosJS),
computed in init; carlosJSAssetPath() is the hashed URL the loader appends.
serveCarlosJS answers two GETs: /__carlos/carlos.js, a tiny loader
revalidated every five minutes (max-age=300, must-revalidate, ETag, 304 on
If-None-Match) that appends a <script> for the hashed asset; and the hashed
asset itself, immutable for a year because its URL changes with its
content. A stale hash under /__carlos/carlos.*.js 404s. Edge mode: the
snippet posts window.onerror and unhandledrejection through the §3.1
sanitisation floor to /__carlos/e, and carlos.track(name, fields) to
/__carlos/a via sendBeacon; it never sends a pageview, since the edge
already counted the page when it served the HTML.

internal/edge/reserved.go: serveReserved now tries serveCarlosJS before its
404 (Task 7 adds the POST dispatch above that).

internal/edge/carlosjs_test.go: TestCarlosJSLoaderAndAsset covers the
loader's headers and revalidation, the asset's headers and verbatim body,
and the stale-hash 404. TestCarlosJSContract pins the snippet's contract as
strings (both error handlers, sendBeacon, the two endpoints, window.carlos,
track:, the three redaction placeholders, location.pathname) and asserts no
"pageview" literal appears. TestCarlosJSNodeSyntax shells out to `node
--check` on the snippet body when node is on PATH, skipping otherwise, so
the JS is parse-checked without a JS toolchain in the build.

Deviation from the brief: the brief's own Step 1 test imports "net/http"
but never references it (httptest is used throughout) — dropped that
import to compile; verified by diff against the brief that nothing else
changed. Also added TestCarlosJSNodeSyntax (not in the brief) as the node
syntax check the task asked for, kept as a real test since it was small,
self-skipping, and needed no fixture beyond t.TempDir().

node --check confirms the snippet parses: both the ad hoc extraction
(sed the const body to a temp file) and the new Go test pass with node
v24.19.0 on PATH.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Paul Campbell pushed by paul@keymail.dev 357920d7793c076fc6cfc25f7b27f5bad05d9e87 parent 00054ea
3 files changed, +170 −0
  • internal/edge/carlosjs.go +86 −0
  • internal/edge/carlosjs_test.go +81 −0
  • internal/edge/reserved.go +3 −0
diff --git a/internal/edge/carlosjs.go b/internal/edge/carlosjs.go
new file mode 100644
index 000000000..99ff2c5f9
--- /dev/null
+++ b/internal/edge/carlosjs.go
@@ -0,0 +1,86 @@
+package edge
+
+import (
+ "crypto/sha256"
+ "encoding/hex"
+ "net/http"
+ "strings"
+)
+
+// carlos.js is vendored here the way the console vendors its icons: in
+// code, never a CDN (spec §3.4). The page includes ONE line —
+//
+// <script src="/__carlos/carlos.js" defer></script>
+//
+// — the loader, revalidated every five minutes, which appends the hashed
+// asset below; the asset is immutable for a year because its URL changes
+// with its content. Edge mode: window.onerror and unhandledrejection post
+// to /__carlos/e after the §3.1 sanitisation floor; carlos.track(name,
+// fields) posts to /__carlos/a via sendBeacon. No pageview is ever sent —
+// the edge counted the page when it served the HTML.
+const carlosJS = `(function(){
+if(window.carlos&&window.carlos.__v)return;
+var E='/__carlos/e',A='/__carlos/a';
+function san(s){s=String(s==null?'':s);if(s.length>8192)s=s.slice(0,8192);
+s=s.replace(/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g,'<email>');
+s=s.replace(/\b(bearer|basic)\s+[A-Za-z0-9._~+\/=-]+/gi,'$1 <redacted>');
+s=s.replace(/\b(token|key|secret|password|passwd|pwd|api_key|apikey|access_token|auth)("?\s*[=:]\s*"?)[^\s&"',;]+/gi,'$1$2<redacted>');
+s=s.replace(/\b[A-Fa-f0-9]{32,}\b/g,'<hex>');
+s=s.replace(/\b(?=[A-Za-z0-9+\/]*\d)(?=[A-Za-z0-9+\/]*[A-Z])(?=[A-Za-z0-9+\/]*[a-z])[A-Za-z0-9+\/]{32,}={0,2}/g,'<hex>');
+return s;}
+function send(u,b){try{var s=JSON.stringify(b);
+if(navigator.sendBeacon){navigator.sendBeacon(u,new Blob([s],{type:'application/json'}));}
+else{fetch(u,{method:'POST',body:s,headers:{'Content-Type':'application/json'},keepalive:true,credentials:'omit'});}}catch(e){}}
+function fields(f){var o={},n=0;if(f&&typeof f==='object'){for(var k in f){if(!Object.prototype.hasOwnProperty.call(f,k))continue;if(n++>=32)break;var v=f[k];if(v==null)continue;
+o[String(k).slice(0,64)]=san(typeof v==='object'?JSON.stringify(v):v).slice(0,256);}}return o;}
+function err(kind,name,stack,f){send(E,{kind:String(kind||'Error').slice(0,64),name:san(name).slice(0,1024),stack:san(stack||'').slice(0,8192),path:location.pathname,fields:fields(f)});}
+window.addEventListener('error',function(ev){var e=ev.error;err(e&&e.name||'Error',ev.message||(e&&e.message)||'error',e&&e.stack||'',{file:ev.filename||'',line:String(ev.lineno||'')});});
+window.addEventListener('unhandledrejection',function(ev){var r=ev.reason;var m=r&&r.message||(typeof r==='string'?r:'unhandled rejection');err(r&&r.name||'UnhandledRejection',m,r&&r.stack||'',{});});
+window.carlos={__v:1,
+track:function(name,f){send(A,{name:String(name||'').slice(0,64),path:location.pathname,fields:fields(f)});},
+error:function(e,f){err(e&&e.name||'Error',e&&e.message||String(e),e&&e.stack||'',f);}};
+})();
+`
+
+var carlosJSHash string
+
+func init() {
+ sum := sha256.Sum256([]byte(carlosJS))
+ carlosJSHash = hex.EncodeToString(sum[:])[:12]
+}
+
+func carlosJSAssetPath() string { return ReservedPrefix + "carlos." + carlosJSHash + ".js" }
+
+// serveCarlosJS answers the loader and the hashed asset. It reports false
+// for any other path so serveReserved can 404 it. HEAD is served like GET
+// (the stdlib drops the body).
+func serveCarlosJS(w http.ResponseWriter, r *http.Request) bool {
+ if r.Method != http.MethodGet && r.Method != http.MethodHead {
+ return false
+ }
+ switch r.URL.Path {
+ case ReservedPrefix + "carlos.js":
+ etag := `"` + carlosJSHash + `"`
+ w.Header().Set("Content-Type", "text/javascript; charset=utf-8")
+ w.Header().Set("Cache-Control", "max-age=300, must-revalidate")
+ w.Header().Set("ETag", etag)
+ w.Header().Set("X-Content-Type-Options", "nosniff")
+ if strings.Contains(r.Header.Get("If-None-Match"), carlosJSHash) {
+ w.WriteHeader(http.StatusNotModified)
+ return true
+ }
+ w.Write([]byte("(function(){var s=document.createElement('script');s.src='" + carlosJSAssetPath() + "';s.async=true;document.head.appendChild(s);})();\n"))
+ return true
+ case carlosJSAssetPath():
+ w.Header().Set("Content-Type", "text/javascript; charset=utf-8")
+ w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
+ w.Header().Set("X-Content-Type-Options", "nosniff")
+ w.Write([]byte(carlosJS))
+ return true
+ }
+ if strings.HasPrefix(r.URL.Path, ReservedPrefix+"carlos.") {
+ http.NotFound(w, r) // a stale hash from an old loader
+ return true
+ }
+ return false
+}
diff --git a/internal/edge/carlosjs_test.go b/internal/edge/carlosjs_test.go
new file mode 100644
index 000000000..5a13d58de
--- /dev/null
+++ b/internal/edge/carlosjs_test.go
@@ -0,0 +1,81 @@
+package edge
+
+import (
+ "net/http/httptest"
+ "os"
+ "os/exec"
+ "strings"
+ "testing"
+)
+
+func TestCarlosJSLoaderAndAsset(t *testing.T) {
+ e := &Edge{}
+ get := func(p string, hdr map[string]string) *httptest.ResponseRecorder {
+ rec := httptest.NewRecorder()
+ req := httptest.NewRequest("GET", "http://app.example.com"+p, nil)
+ for k, v := range hdr {
+ req.Header.Set(k, v)
+ }
+ e.serveReserved(rec, req, "app.example.com", upstream{})
+ return rec
+ }
+ rec := get("/__carlos/carlos.js", nil)
+ if rec.Code != 200 || rec.Header().Get("Cache-Control") != "max-age=300, must-revalidate" {
+ t.Fatalf("loader: %d %q", rec.Code, rec.Header().Get("Cache-Control"))
+ }
+ if !strings.Contains(rec.Body.String(), carlosJSAssetPath()) {
+ t.Fatal("loader must reference the hashed asset")
+ }
+ if ct := rec.Header().Get("Content-Type"); !strings.HasPrefix(ct, "text/javascript") {
+ t.Fatalf("content-type %q", ct)
+ }
+ etag := rec.Header().Get("ETag")
+ if etag == "" {
+ t.Fatal("loader carries an ETag so revalidation is a 304")
+ }
+ if rec := get("/__carlos/carlos.js", map[string]string{"If-None-Match": etag}); rec.Code != 304 {
+ t.Fatalf("revalidate: %d", rec.Code)
+ }
+ rec = get(carlosJSAssetPath(), nil)
+ if rec.Code != 200 || rec.Header().Get("Cache-Control") != "public, max-age=31536000, immutable" {
+ t.Fatalf("asset: %d %q", rec.Code, rec.Header().Get("Cache-Control"))
+ }
+ if rec.Body.String() != carlosJS {
+ t.Fatal("asset body is the snippet verbatim")
+ }
+ if rec := get("/__carlos/carlos.deadbeef0000.js", nil); rec.Code != 404 {
+ t.Fatalf("stale hash: %d", rec.Code)
+ }
+ if len(carlosJSHash) != 12 {
+ t.Fatalf("hash %q", carlosJSHash)
+ }
+}
+
+// The snippet's contract (spec §3.4), pinned as strings: handlers for both
+// error events, sendBeacon for events, no pageview send in edge mode.
+func TestCarlosJSContract(t *testing.T) {
+ for _, want := range []string{"'error'", "'unhandledrejection'", "navigator.sendBeacon", "/__carlos/e", "/__carlos/a", "window.carlos", "track:", "<email>", "<redacted>", "<hex>", "location.pathname"} {
+ if !strings.Contains(carlosJS, want) {
+ t.Errorf("snippet lacks %s", want)
+ }
+ }
+ if strings.Contains(carlosJS, `"pageview"`) || strings.Contains(carlosJS, `'pageview'`) {
+ t.Error("the snippet must not send pageviews in edge mode")
+ }
+}
+
+func TestCarlosJSNodeSyntax(t *testing.T) {
+ nodePath, err := exec.LookPath("node")
+ if err != nil {
+ t.Skip("node not on PATH")
+ }
+ dir := t.TempDir()
+ file := dir + "/carlos.js"
+ if err := os.WriteFile(file, []byte(carlosJS), 0o644); err != nil {
+ t.Fatal(err)
+ }
+ out, err := exec.Command(nodePath, "--check", file).CombinedOutput()
+ if err != nil {
+ t.Fatalf("node --check: %v\n%s", err, out)
+ }
+}
diff --git a/internal/edge/reserved.go b/internal/edge/reserved.go
index 24aa1b2af..ee2153a42 100644
--- a/internal/edge/reserved.go
+++ b/internal/edge/reserved.go
@@ -20,5 +20,8 @@ const reservedSegment = "__carlos"
// dispatch inside it; host and up are already threaded through because both
// need them for attribution.
func (e *Edge) serveReserved(w http.ResponseWriter, r *http.Request, host string, up upstream) {
+ if serveCarlosJS(w, r) {
+ return
+ }
http.NotFound(w, r)
}