CSP and CORS
This is the developer experience win that drove Agreely's design: you can use a
strict Content Security Policy without adding unsafe-inline.
Minimal CSP additions
Add these three directives to your existing policy:
script-src https://cdn.agreely.ca ;
connect-src https://cdn.agreely.ca ;
img-src https://cdn.agreely.ca ;
That is all. No wildcard. No unsafe-inline, for scripts or styles.
| Directive | Why |
|---|---|
script-src https://cdn.agreely.ca |
The loader and the banner module are served from this origin. |
connect-src https://cdn.agreely.ca |
The banner fetches configuration and submits consent. The module origin must appear in connect-src because SRI requires a CORS (crossorigin) fetch. |
img-src https://cdn.agreely.ca |
Your company logo (optional) is served from the same domain. |
Why no unsafe-inline is needed
The banner styles are applied to the shadow root (Shadow DOM) via a
constructable CSSStyleSheet (replaceSync + adoptedStyleSheets). There is
no inline <style> element, no inline style attribute, and no eval. A
conforming browser that supports Shadow DOM v1 and constructable stylesheets does
not need you to loosen your style-src. This design directly answers the problem
the founder encountered with a competing solution (Byscuit).
Nonce passthrough
If your script-src is nonce-based rather than host-based, pass your nonce via
data-nonce on the loader tag (see Installation). The
loader forwards it to the injected module script automatically.
SRI on pinned versions
For security-hardened deployments that pin an exact version, use the versioned URL and verify the published hash in our version manifest:
<script async
src="https://cdn.agreely.ca/e/v1.2.3/loader.js"
data-agreely="agr_pub_YOUR_KEY"></script>
Versioned URLs are immutable (Cache-Control: immutable, max-age=31536000) and
the loader pins the exact sha384 SRI of the matching build's banner module. The
manifest for each version is published at
https://cdn.agreely.ca/e/v1.2.3/manifest.json.
No CORS setup required
The public /e/v1/config/{key} endpoint responds with
Access-Control-Allow-Origin: * (public configuration, no personal information).
The consent submission endpoint (POST /e/v1/consent) uses a text/plain body
with no custom headers, making it a simple CORS request: no OPTIONS
preflight is issued and no server-side CORS configuration is needed on your end.
In short: you add three lines to your CSP; you configure nothing server-side.
Full policy example
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-abc123' https://cdn.agreely.ca https://www.googletagmanager.com;
connect-src 'self' https://cdn.agreely.ca https://www.google-analytics.com;
img-src 'self' data: https://cdn.agreely.ca;
style-src 'self' 'nonce-abc123';
frame-src https://www.youtube.com;
Tip: if you also use Google Tag Manager, add
https://www.googletagmanager.comtoscript-srcand place the Agreely tag before the GTM tag (see Consent Mode v2).