# 16 · Analytics Events, Site-Brain Records, and Logging

Read this when an extension contributes events to Cedros analytics or when
extension activity should be durably visible to operators and AI agents.

Three telemetry channels exist, each with a different job:

| Channel | Job | Emit path |
|---|---|---|
| Analytics events | owner-actionable product metrics | browser helper or wasm `telemetry.emit-event` |
| Site-brain records | durable operational history operators and AI agents read | wasm `telemetry.emit-record` (capability-scoped) |
| Structured logs | transient diagnostics | wasm `logging.log` |

Canonical wasm contract: [`07-wasm-server-backends.md`](07-wasm-server-backends.md).

## Analytics events

- Event declarations live under `manifest.analytics.events`
  ([field reference](04-manifest-reference.md)).
- Event ids must be stable and namespaced with `extensionId`.
- Event visibility is `public`, `authenticated`, or `admin`.
- Event `kind` is optional. Use it only when Cedros analytics should roll the
  event into a semantic tile. Recognized values: `purchase_intent_view`,
  `purchase_completion`, `subscription_start`, `subscription_cancellation`,
  `signup_completion`, `lead_capture`.
- Public web code reports through the host-injected
  `window.cedrosAnalytics.trackExtensionEvent` API.
- Server routes/jobs emit through wasm
  `telemetry.emit-event(event-id, label, metadata-json)`, granted when the
  manifest declares any `surfaces.server.events`.
- Cedros analytics decorates recorded extension event counts from manifest
  metadata.

Runtime API status:

| Runtime seam | Status | If unavailable |
|---|---|---|
| analytics browser report | self-serve through `window.cedrosAnalytics.trackExtensionEvent` on host-rendered public pages | declare metadata + trigger map |
| analytics server report | available through wasm `telemetry.emit-event` | package must include `cedros-extension.server.wasm` |

Browser reporting is self-serve. Both browser helpers respect the
`extensionEvents` analytics setting and forward to configured third-party
analytics integrations; the server accepts `eventType: "extension"` on the
public analytics endpoint.

**Server declarations are enforced per call:** `telemetry.emit-event` rejects
an event id that is absent from `surfaces.server.events[]`. Keep browser event
ids aligned with `analytics.events[]`; the browser helper and public endpoint
have their own intake/context checks. Emission can silently no-op when the
site's analytics settings disable extension events, so a successful call is
not proof the event was recorded.

It is also not a durable Cedros Notifications ingestion acknowledgment. An
enabled `setupSeeds.notifications[]` route may receive a best-effort host
dispatch after analytics emission, but dispatch errors are not returned through
`telemetry.emit-event` and no extension-facing delivery status is available.
Do not use telemetry success to advance a notification-dependent workflow. The
requested distinct publish/status family is explicitly unavailable; see
[`contracts/operational-wallet-notification-seams.md`](contracts/operational-wallet-notification-seams.md).

Event design:

- start from the product questions the events should answer; do not declare
  events the owner cannot act on
- document the trigger map (which control/route/job emits which event) and the
  safe property shape — property schemas are handoff/runtime docs unless
  Cedros publishes a validated `properties` manifest field
- public events must be safe without authenticated context
- metadata must be small, safe JSON

Analytics rules:

- Do not log tokens, emails, raw PII, cookies, auth headers, or secrets.
- Do not create private per-extension analytics storage when Cedros needs
  shared reporting.
- Keep event ids stable and machine-joinable; use operator-facing
  descriptions.
- Do not use `kind` as a replacement for precise event ids or categories; it
  is only a dashboard rollup hint.
- Deprecate event ids before removing them, and never reuse an id with a new
  meaning.

## Site-brain records

Durable operator and AI-agent activity goes through the shared site-brain
seam: `telemetry.emit-record(category, title, text, payload-json)` (payload
may carry `summary`/`subject`/`tags`/`details`), attributed to the extension.
`surfaces.server.events` links the telemetry interface, but durable writes are
denied unless the manifest also declares a `site-brain-record:<category>`
write scope in root `capabilities[]`.

- Important operational actions should emit site-brain records, not only
  transient logs.
- Cedros may publish extension platform and AI metadata into site-brain
  runtime snapshots through host-owned indexing; extension packages do not
  write those snapshots directly.
- There is no private per-extension brain/audit feed.
- Reference objects (kind, id/path/url, label, redacted description) are
  plan/handoff documentation only: the wasm `emit-record` payload accepts
  `summary`/`subject`/`tags`/`details` only, and the host sets the record's
  references empty.
- If a non-wasm/browser/admin site-brain emitter API is not available through a
  documented host binding, do not invent one. Produce the durable record plan and mark that
  runtime emission as host-coordinated.

Durable record example (shape reference:
[`04-manifest-reference.md`](04-manifest-reference.md)):

```json
{
  "category": "sync",
  "subject": "acme-demo:nightly-sync",
  "title": "Nightly sync completed",
  "summary": "Synced 12 records with 1 skipped item.",
  "tags": ["acme-demo", "sync"],
  "details": {
    "syncedCount": 12,
    "skippedCount": 1,
    "runId": "redacted-run-ref"
  }
}
```

Record rules:

- Keep category, subject, and tags stable lowercase identifiers.
- Keep durable text concise and human-readable; keep titles and summaries
  short enough for dashboard/support surfaces (one short sentence / one or two
  short sentences), and move long details into redacted `details` JSON.
- AI agents should be able to understand the record without reading private
  logs.

## Structured logs

Transient process logs go through wasm `logging.log` (always granted). They
should be structured, contextual, and redacted. Guest log lines are attributed
to the extension, control-character-stripped, and truncated at 4 KiB.
When the message is a JSON object, the host promotes its top-level fields into
the operational log record: `message` becomes the display message, `surface`
drives the Observability filter, and other fields remain searchable details.
Host-side redaction still replaces sensitive keys at any nested depth; guests
must also avoid sending secrets or unnecessary personal data in the first place.

Redacted transient log example:

```json
{
  "level": "warn",
  "message": "Nightly sync skipped one record.",
  "extensionId": "acme-demo",
  "surface": "job",
  "operation": "nightly-sync",
  "runId": "redacted-run-ref",
  "cause": "validation_failed",
  "metadata": {
    "skippedCount": 1
  }
}
```

## Choosing the channel

| Situation | Analytics | Site-brain | Transient log |
|---|---:|---:|---:|
| Owner clicks dashboard action | yes, if actionable | maybe | yes |
| Setup completed | maybe | yes | yes |
| Job succeeds with owner-visible result | maybe | yes | yes |
| Job transient retry | no | maybe | yes |
| Provider call made | usage attribution | no unless material | yes, redacted |
| Secret saved/rotated | no | yes | yes, redacted |

## Redaction rules (all channels)

- Never log secrets, tokens, keys, cookies, auth headers, emails, phone
  numbers, or raw PII.
- Prefer redaction over dropping all context.
- Do not rely on console logs for owner-visible operational history.

## Acceptance checks

- Local package intake accepts valid events and rejects invalid namespaces.
- Analytics reports can map recorded event ids back to display names,
  categories, and semantic kinds when present.
- Event metadata is redacted and contains no direct identifiers.
- Public events respect analytics settings and privacy constraints.
- Important install/setup/sync/execution outcomes have durable records when
  operators need them; details JSON excludes direct identifiers and secrets.
- Transient logs include context and cause without sensitive values.

Next: [`17-ai-skills-and-capabilities.md`](17-ai-skills-and-capabilities.md).
