Skip to content

Embed script

The Sill embed script is a single, self-contained <script> tag a merchant drops into their site. It runs once per page load, classifies the visiting client against Sill’s seeded identity registry, POSTs one beacon describing the visit to Sill’s edge, and advertises the site’s signed agent card and ARD catalog to DOM-parsing agents. It uses no cookies, no localStorage, no fingerprinting primitives, and is wrapped in a top-level try/catch — a script failure is invisible to the host page.

Paste this immediately before the closing </body> of pages you want to instrument. Replace sk_… and pf_… with the values from your site’s dashboard.

<!-- paste before </body> -->
<script async src="https://cdn.sill.so/embed.js"
data-site-key="sk_…"
data-proof-token="pf_…"></script>

That is the whole install for Discovery mode. There is no SDK to vendor, no platform plugin to maintain, and no Sill code in your bundle. The script is served from a Cloudflare-cached origin, with the size budget enforced at build time at 14 kB gzip (a CI assertion blocks any change that exceeds it).

The async attribute is required (or defer — either is accepted; a plain <script> is rejected at the dashboard install-instructions surface). On async load, the script schedules itself for the next idle frame after the document reaches interactive, so it never competes with first paint.

data-proof-token — what it is, what it is not

Section titled “data-proof-token — what it is, what it is not”

data-proof-token is a per-site, opaque, public-by-design value Sill’s origin uses to confirm the snippet is actually present in your served HTML — an anti-spoofing ownership proof for the domain.

The embed runtime does not read or transmit data-proof-token. It exists solely so Sill’s origin can server-fetch your published page and look for the attribute in the markup. If we find it, we know the snippet is installed at the domain that claims the site_key; if we don’t, the install stays in a pending state until we do. The attribute is safe to commit to source, just like the site key itself.

The data-site-key value is a public credential. It is intentionally embedded in client-side HTML where any visitor can read it, much like a Stripe publishable key (pk_...). It identifies which Sill site the beacon is for; it does not authorize anything sensitive on its own.

Two accepted shapes (both validated against ^(sk_)?[A-Za-z0-9_-]{43}$ at the edge):

  • sk_<43> (46 characters) — the raw site_key from the dashboard’s manual install snippet.
  • <43> (43 characters) — the bare site_key_hash, used by the Shopify auto-install which ships the key via the script’s own ?site=<hash> query parameter.

A malformed or missing key triggers a single console.warn and the script aborts silently — no beacon, no DOM changes. That is the only console message the script ever emits.

The script POSTs exactly one JSON body per page load to https://edge.sill.so/v1/discovery/ingest, using navigator.sendBeacon where available and fetch({ keepalive: true, credentials: 'omit' }) as a fallback. The transport is fire-and-forget: no retries, no credentials, no response handling, all errors swallowed.

The wire shape (schema_version "1"):

{
"site_key": "sk_AbCdEfGhIjKlMnOpQrStUvWxYz0123456789-_ABCDE",
"schema_version": "1",
"observed_at": "2026-06-22T14:08:11.412Z",
"identification": {
"user_agent": "Mozilla/5.0 ...",
"client_hints": {
"sec_ch_ua": "\"Chromium\";v=\"126\", \"Not.A/Brand\";v=\"24\"",
"sec_ch_ua_mobile": "?0",
"sec_ch_ua_platform": "\"macOS\""
},
"agent_card_claim": { "jws_compact": "eyJhbGciOiJFZERTQSI..." },
"referrer_origin": "https://example-merchant.com"
},
"page_context": {}
}

Every field inside identification is optional and is omitted when absent on the host page.

The script reads from precisely these places — nothing else:

SourceBecomes
navigator.userAgent (capped at 512 bytes)identification.user_agent
navigator.userAgentData.brands / .mobile / .platform (Chromium only)identification.client_hints.sec_ch_ua*
<meta name="sill-agent-card" content="...">identification.agent_card_claim.jws_compact (verbatim, not parsed)
<meta name="sec-ch-ua-sill-agent" content="...">identification.client_hints.sec_ch_ua_sill_agent
location.originidentification.referrer_origin

By design, the script does not read or transmit:

  • Cookies, localStorage, or sessionStorage.
  • Form data, input values, or the contents of the page DOM.
  • URL query parameters or URL path.
  • document.referrer.
  • navigator.plugins, navigator.languages, screen dimensions.
  • Canvas, WebGL, audio, or other fingerprinting primitives.
  • High-entropy User-Agent Client Hints (getHighEntropyValues() is never called).
  • IP address — that is observed server-side by the edge from the TCP connection itself; the script does not attempt to read or send it.
  • data-proof-token — see the install-snippet section above.

There is no Discovery-mode setting that adds any other data source. The list above is the entire universe of what the embed can see.

sequenceDiagram
    participant Browser
    participant Edge as edge.sill.so

    Browser->>Edge: GET cdn.sill.so/embed.js
    Edge-->>Browser: 200 (async script)
    Note over Browser: readyState → interactive,<br/>then requestIdleCallback
    Note over Browser: read + validate site_key
    Browser->>Browser: inject <link rel="agent-card">
    Browser->>Browser: inject <link rel="ai-catalog">
    Browser->>Edge: POST /v1/discovery/ingest
    Note over Browser,Edge: fire-and-forget (response not read)

The two <link> tags injected into <head> advertise the site’s signed manifests to agents that parse the DOM:

<link rel="agent-card"
type="application/json"
href="https://edge.sill.so/v1/agent-card/<site_key>.json">
<link rel="ai-catalog"
type="application/json"
href="https://edge.sill.so/v1/catalog/<site_key>.json">

Both are built with createElement + setAttribute (never innerHTML), are idempotent against pre-existing tags, and are emitted before the beacon — so a beacon failure cannot suppress agent discovery. Both endpoints serve Sill-signed payloads anyone can verify against Sill’s public JWKS.

The script is CSP-safe: no eval, no new Function(), no document.write, no inline event handlers, no dynamic <script> injection. If your site sends a CSP, allow Sill’s edge in the relevant directives:

Content-Security-Policy:
script-src 'self' https://cdn.sill.so;
connect-src 'self' https://edge.sill.so;

You do not need 'unsafe-inline', 'unsafe-eval', or 'strict-dynamic'.

The script ships with public-cacheable headers so the browser and any intermediate cache can hold it for a short window. To force a faster invalidation, append a version query string to the install tag:

<script async src="https://cdn.sill.so/embed.js?v=2026-06-22"
data-site-key="sk_…"
data-proof-token="pf_…"></script>

The query string is ignored by the edge; it only changes the browser’s cache key.

A quick fetch confirms the script is reachable and cacheable from your network:

Terminal window
curl -I https://cdn.sill.so/embed.js

You can also send a beacon by hand from a server-side context using the published HTTP endpoint:

Terminal window
curl -X POST https://edge.sill.so/v1/discovery/ingest \
-H 'content-type: application/json' \
-d '{
"site_key": "sk_…",
"schema_version": "1",
"observed_at": "2026-06-22T14:08:11.412Z",
"identification": { "user_agent": "test/1.0" },
"page_context": {}
}'

Once the script is on a page, a successful install surfaces in the dashboard within a few page loads.

Dashboard install panel — the snippet is generated from your site key, copyable, and shows install status against the proof token.

Silent, by design. Every code path is wrapped in a top-level try/catch. The script is invisible to the host page even on failure — a dropped beacon is a lost identification event, accepted explicitly rather than retried. The single permitted console output is a console.warn if data-site-key is malformed; that is a misconfiguration the merchant should see in their browser console.

Does it slow down my site? The tag loads async, so it never blocks parsing or first paint. The beacon itself is scheduled via requestIdleCallback (with setTimeout(0) fallback) after the document reaches interactive. There is no rendering work and no synchronous DOM mutation on the host page beyond appending two <link> tags inside <head>.

Will it set cookies? No. The transport explicitly sends credentials: 'omit', and the script never touches document.cookie, localStorage, or sessionStorage. There is no Sill-side cookie, ever.

Do I need to install it on every page? Install it wherever you want agent traffic identified — typically the site-wide layout. Each page load fires one beacon; cross-page deduplication is handled server-side.

What does data-proof-token do? It is the per-site, opaque, public-by-design value Sill’s origin uses to confirm the snippet is installed at your domain (ownership proof). The browser-side embed runtime does not read it or transmit it anywhere — it exists only so the origin’s proof-check server-fetch can find it in your served HTML.

Is there a no-JavaScript fallback for the agent card? Yes. You can paste the <link rel="agent-card"> tag directly in <head> so HTML-only agent crawlers see the pointer without executing the embed. The runtime injection is idempotent — it skips when a link[rel="agent-card"] already exists. See agent card.

Can I host the script myself? No. The script is served from Sill’s edge so its constants stay in lock-step with the ingest endpoint’s accepted wire shape (the schema version, the site-key regex, the ingest URL). A self-hosted copy would drift the first time either side updates.