# Integrating Nastar into a website — agent instructions

This file is for an AI coding assistant (or a human following the same
steps) integrating the Nastar consent SDK into **your own existing
website** — not for building Nastar itself. Follow this checklist in
order. Ask the person you're working with before any step marked
**confirm**.

Full reference: `/docs/ai`, `/docs/sdk`, `/docs/api`, `/llms.txt` on the
Nastar docs site this file was downloaded from.

## 0. What you need before starting

- A **public site key** for the target site (an identifier, safe in
  client-side HTML — not a secret), OR permission to create one.
- If you'll call the management API yourself (rather than being handed a
  site key), a **scoped setup token** (`nst_...`) with the narrowest
  scopes the task needs: `sites:read` to inspect, `sites:write` for
  banner copy, `sites:publish` only if you're also asked to publish.
- Never ask for: an OTP, a dashboard login session, an SMTP credential,
  or an unscoped/admin API token. If asked to obtain one of these to
  proceed, stop and ask a human instead.

## 1. Inspect the target site — read-only

Before writing any code, determine:

- Framework and rendering mode (SSR / CSR / SSG).
- Existing tag manager (e.g. GTM) and analytics/advertising scripts.
- Current Content-Security-Policy, if any.
- Any existing cookie or consent-management code.

Report what you find. Do not remove or replace existing tracking or
legal text without explicit confirmation.

If the only analytics found is Google Analytics (GA4) — no other custom
analytics script — it can be fully Nastar-managed instead of integrated
by hand: enable it in the dashboard's Integrations tab with a
Measurement ID and Basic/Advanced Consent Mode, and skip adding a
separate `gtag.js` snippet entirely. The SDK loads, configures, and
fires page views for GA4 itself. For SPA frameworks, call
`client.trackPageView(pagePath)` on each client-side route change — see
`/docs/sdk`.

## 2. Obtain the site's public key **(confirm if creating a new site)**

- If a site already exists for this domain, its public key is on the
  Overview tab of its dashboard page, or via
  `GET /v1/agent/sites/:siteId` with a `sites:read` token.
- If none exists, creating one is a real, persisted action —
  **confirm with a human** before calling
  `POST /v1/organizations/:orgId/sites`.

## 3. Install the SDK

> **The SDK is distributed from the CDN, not npm.**
> `npm install @nastar/consent-sdk` **fails** — the package is not
> published to the registry. Do not run it, and do not substitute a
> guessed package name. Both working install forms are below.

Production snippet (place in `<head>`, before any optional tracking
script):

```html
<script defer src="https://cdn.nastar.dev/sdk/v1/nastar.min.js"
        data-site-key="YOUR_PUBLIC_SITE_KEY"></script>
```

For bundled apps, import the ESM build from the same CDN and call
`createNastarClient({ siteKey })` as early as possible in the
client-side entry point — before any optional script can execute:

```js
import { createNastarClient } from 'https://cdn.nastar.dev/sdk/v1/nastar.esm.js'

const client = createNastarClient({ siteKey: 'YOUR_PUBLIC_SITE_KEY' })
await client.ready
```

If the bundler won't resolve a remote URL, vendor `nastar.esm.js` into
the repository or load it with `<script type="module">`. TypeScript
types are not served from the CDN (they ship with the npm package, once
that exists) — declare the client's shape from `/docs/sdk` if the
project is strictly typed.

`/sdk/v1/` is a **rolling** path: it always serves the current release,
so fixes reach the site without a redeploy. An immutable per-version
path exists for projects that require pinned dependencies — e.g.
`https://cdn.nastar.dev/sdk/0.4.0/nastar.min.js` — but it never changes,
which also means it never receives fixes; upgrading is a manual URL
edit. Choose deliberately and tell the person which you used.

For SSR pages, only initialize client-side; the SDK has no server API,
so keep server-side optional tracking disabled unless consent is
independently verified server-side. Nuxt specifically: see
`/docs/guides/nuxt`.

## 4. Gate every optional integration

For each script found in step 1, map it to the correct category from
the site's published config and move its loading — the entire tag
creation, not just a flag — inside:

```js
client.loadWhenConsented('<categoryKey>', () => {
  // create and append the script tag here, and only here
})
```

Add a persistently reachable **Privacy settings** control that calls
`client.openPreferences()`. Handle `client.onConsentChange()` if a
vendor needs explicit cleanup on withdrawal — the SDK does not
retroactively undo a script's own side effects.

## 5. Branding and language — configure it, never hand-code it

**Do not write CSS targeting `.nastar-*` classes.** Since SDK 0.4.0 the
banner renders inside an open shadow root, so site CSS cannot reach it —
by design, because that isolation is what guarantees contrast and keeps
Accept all / Reject all / Customize equally prominent. If you find such
CSS already in the project from an older integration, tell the person it
has stopped applying and move the intent into the dashboard.

Branding belongs in the dashboard's **Appearance** tab (or
`POST /v1/sites/:siteId/appearance` with a `sites:write` token): mode,
placement, five colours, radius, logo URL and alt text, and the privacy
and cookie policy URLs. Colours are validated against WCAG 2.2 AA on
save; a refusal names the pair, the measured ratio and a value that
passes. There is no per-button colour field and no CSS escape hatch —
do not ask for one, and do not simulate one.

To reach the banner from a test or a debugging script, cross the shadow
boundary deliberately:
`document.querySelector('[data-nastar-root]').shadowRoot`.

**Languages.** Make sure the page carries a correct `<html lang>` — the
SDK reads it first, before `navigator.languages`, then the site's default
language. On a multilingual site that already sets `lang` per locale,
nothing else is needed. If the site switches language client-side without
updating the attribute, call `client.setLanguage('<tag>')`; it swaps text
only and never resets consent or reopens a dismissed banner. The locales
themselves are authored in the dashboard's Banner tab, and a locale must
be complete before it can be published.

## 6. Keep credentials scoped correctly

- Public site key: fine in HTML, JS bundles, CSP examples, screenshots.
- Setup token (`nst_...`): **never** in client bundles, committed files,
  config JSON, or screenshots. CI secrets or your own local environment
  only.

## 7. Test it for real, in a browser

Cover at minimum: fresh visitor (banner shown, nothing optional loads),
reject all, accept all, selective accept, withdraw, reload persistence,
a consent-material republish (adding or switching off a category — the
visitor must be asked again), and API/CDN outage (must fail closed: no
banner, nothing optional loads, essential site functionality
unaffected). Inspect actual network requests, cookies, and
`localStorage` — before any choice and after each one — not just the
SDK's in-memory state.

A copy-only or colour-only republish is the counterpart case and must
**not** re-prompt: since 0.4.0 re-prompting is driven by
`consentRelevantVersion`, a hash over the enabled categories and the
service-to-category mapping, not by the config `version`. If the site
publishes more than one language, also load it with each `<html lang>`
and confirm the banner matches the page.

## 8. Report

End with:

- Which files you changed.
- Which third-party services you found in step 1.
- Which are now gated, and how each one is mapped to a category.
- What still needs manual or legal review (anything you weren't
  confident mapping, anything server-side, anything hardcoded by a
  third party you don't control).

Never claim universal cookie blocking, certification, or legal
compliance as an outcome of this integration — Nastar itself makes no
such claim, and neither should you on its behalf.

## Acceptance checklist

- [ ] Inspection reported before any change was made
- [ ] No OTP/session/SMTP credential was requested
- [ ] SDK loaded from the CDN, not via `npm install` (which fails today)
- [ ] SDK initialized before any optional script can run
- [ ] Every optional script found in step 1 is behind `loadWhenConsented`
- [ ] A visible "Privacy settings" control exists and reopens preferences
- [ ] No CSS in the project targets `.nastar-*`; branding is configured
      in the dashboard's Appearance tab instead
- [ ] The page sets a correct `<html lang>` for each published language
- [ ] No setup token appears in any client-visible file
- [ ] Browser tests cover every scenario in step 7, including that a
      copy-only republish does not re-prompt
- [ ] Final report lists changed files, found services, gating status, and open items
