# Syncanix AI-Content Provenance — detection specification (`syncanix-content-provenance/v1`)

**Status:** canonical spec text, v1 (ADR-0173). This document is the repo source of the
public specification served at `https://syncanix.com/.well-known/syncanix-content-provenance`
(publication + the key set ride the provenance-key provisioning deploy). It is the EU AI
Act **Article 50(2)** machine-readable-marking detection affordance in the form the
Transparency Code of Practice permits: _a public standard or specification that can be
implemented by third parties_ — anyone can verify Syncanix marks with this document and
the published public key, free of charge.

## 1. What Syncanix marks

Syncanix marks **LLM-generated content only**. Human-authored platform copy (refusal and
handoff templates, transactional email), end-user messages, and customer API data
returned by tools are **never** marked — over-marking is treated as an error equal to
under-marking. The marks:

| Surface                               | Mark                                                                           |
| ------------------------------------- | ------------------------------------------------------------------------------ |
| Chat SSE stream + resume responses    | HTTP response header `X-Syncanix-AI-Generated: true`                           |
| Rendered assistant text in the widget | DOM attribute `data-syncanix-ai-generated="true"` on the generated-text node   |
| Streamed assistant messages           | Signed provenance object on the `message.end` event (§ 2)                      |
| Conversation-forwarding deliveries    | `X-Syncanix-AI-Generated: true` request header                                 |
| Escalation (handoff) webhooks         | Same header, set only when the payload carries assistant excerpts              |
| Conversation exports (JSONL)          | First line = `syncanix-export-manifest` object with a `provenance` block (§ 3) |
| DSAR portability bundles              | Top-level `provenance` object (§ 3)                                            |

## 2. The signed per-message mark

Attached as the optional `provenance` field of the `message.end` stream event:

```json
{
  "v": 1,
  "kind": "ai-generated",
  "generator": "syncanix",
  "contentHash": "<64 lowercase hex chars>",
  "timestamp": "<ISO-8601 UTC>",
  "sig": "<base64url, no padding>",
  "keyId": "<key-set entry id>"
}
```

- `contentHash` — SHA-256 (lowercase hex) of the UTF-8 bytes of the full message text,
  i.e. the concatenation of every `message.delta` string for that `messageId`, in stream
  order, with no separators or normalization.
- `sig` — an **Ed25519** signature over the UTF-8 bytes of the exact string
  `1|ai-generated|syncanix|<contentHash>|<timestamp>`, base64url-encoded without padding.
- `keyId` — selects the verification key from the published key set.

**Verification procedure:** (1) recompute `contentHash` from the message text you
received; (2) reject if it differs from `mark.contentHash`; (3) build the payload string
above; (4) verify `sig` with the Ed25519 public key named by `keyId` from the key set at
the canonical URL. Reference implementation: `@syncanix/shared/provenance-sign`
(`verifyMessageProvenance` — total, returns false on any malformed input).

**Key set:** published alongside this spec as JSON `{ "keys": [{ "keyId", "alg": "Ed25519",
"publicKeyPem", "notBefore" }] }`. Key rotation adds an entry; marks always verify
against the key their `keyId` names.

## 3. Export-artifact notices

Non-empty conversation exports (JSONL) begin with one manifest line:

```json
{
  "kind": "syncanix-export-manifest",
  "version": 1,
  "provenance": {
    "aiGenerated": { "roles": ["assistant"] },
    "marking": "syncanix-content-provenance/v1",
    "spec": "https://syncanix.com/.well-known/syncanix-content-provenance"
  }
}
```

DSAR portability bundles carry the same `provenance` object at the top level. In both,
the assertion is per-role: entries whose `role` is `assistant` are AI-generated;
`user`/human-agent entries are not.

## 4. Limits (stated per Art 50(2)'s feasibility clause)

Text copied out of a rendered surface by a human loses the delivery-layer marks — an
acknowledged limitation of text as a medium, shared by every text-generating provider;
the layered visible disclosure (Art 50(1), always-on in the widget) is the complementary
control. Marks attest provenance at generation/delivery time; they make no claim about
subsequent human editing.

## 5. Versioning

This is `syncanix-content-provenance/v1`. Any change to the signing payload, hash
construction, or mark shape is a new version served alongside v1 with its own spec
section; `v` in the mark and the spec id disambiguate. The constants in this spec are
pinned by content-contract tests in the Syncanix codebase
(`packages/shared/src/provenance/ai-content-marking.test.ts`,
`packages/shared/src/provenance-sign/ai-provenance.test.ts`).
