# Wire Proof into this app

You are a coding agent. This page is the whole procedure. Run it start to finish,
then report what you measured.

You need two keys from the workspace owner:

- `WRITE_KEY` (starts `pk_`) — public, ships in page HTML, gates ingest only.
- `AGENT_KEY` (starts `ak_`) — secret, server-side only, never in client code.

Base URL for everything below: `https://proof.dorsr.com`

---

## The rule that matters

**Do not report success until step 5 returns a number you did not write.**

Every other analytics installer stops at "instrumentation added". That is the step
where a wrong emit becomes a wrong number nobody catches, because a typo'd event
name looks exactly like no traffic. Proof's door rejects events that violate their
contract and records the refusal, so you can verify your own work instead of
assuming it. Finish the loop.

---

## 1. Add the tag

Once, in the root layout. Next.js `app/layout.tsx`, Vite `index.html`, or the
equivalent `<head>` for your framework.

```html
<script src="https://proof.dorsr.com/p.js" data-key="WRITE_KEY" defer></script>
```

Autocaptured from this alone: pageviews, sessions, clicks, Core Web Vitals, JS
errors, outbound clicks, SPA route changes.

Dev hosts (localhost, bare IPs, `*.local`) are ignored unless the tag carries
`data-dev`, so local work never pollutes production truth.

## 2. Mark the 404 page

In whatever component renders "not found":

```html
<meta name="proof-not-found" content="1">
```

Without this, a missing path looks like a normal pageview and dead links stay
invisible.

## 3. Track the events that matter to this product

Autocapture gets you traffic. It cannot know what *this* app is for. Read the
codebase, find the moments that represent real value, and instrument those.

```js
window.proof.track("myapp.checkout_done", { plan: "pro" })
```

Naming: `namespace.thing_happened`, snake_case, dot-separated. Use one namespace
for the whole app.

Pick the boundaries a founder would ask about: signup completed, key saved, run
started, item published, subscription started. Do not instrument every click; you
already have clicks.

Properties should be low-cardinality facts about the event (`plan`, `source`,
`kind`), never free text, never anything personal. If a value could identify a
person, do not send it.

## 4. Propose a contract for every event you added

An event with no contract is stored but ungoverned: nothing certified can depend
on it, and it shows up on the owner's dashboard as an unfinished job. Propose one
for each name from step 3.

```bash
curl -s -X POST https://proof.dorsr.com/api/propose \
  -H "x-proof-key: AGENT_KEY" -H "content-type: application/json" \
  -d '{"kind":"event_definition","name":"myapp.checkout_done","version":1,
       "humanMeaning":"A customer completed a checkout.",
       "properties":{"plan":{"type":"enum[free,pro]","required":true}}}'
```

`humanMeaning` is one sentence a non-engineer can read and say "yes, that is
true" or "no, that is not what that means". It is the thing they actually sign.
Write it for them, not for you.

You can propose. You cannot sign. The owner (or an agent holding their named,
revocable delegation grant) certifies it, and the stamp records who signed.

## 5. Verify. This is the step that matters.

Deploy. Load one page in a real browser. Trigger one of the events from step 3.
Then ask Proof what it saw:

```bash
curl -s -X POST https://proof.dorsr.com/api/ask \
  -H "x-proof-key: AGENT_KEY" -H "content-type: application/json" \
  -d '{"question":"pageviews today"}'
```

The response carries a receipt: the exact SQL that ran, how many rows it scanned,
a confidence flag, and what it cost. Read it. If pageviews are zero, the tag is
not loading, and you are not done.

Now check that nothing you sent was refused:

```bash
curl -s -X POST https://proof.dorsr.com/api/ask \
  -H "x-proof-key: AGENT_KEY" -H "content-type: application/json" \
  -d '{"question":"is my tracking broken"}'
```

This reads the ingest door's ledger of what it refused and what arrived
ungoverned. A refused event was never stored, so this is the only place it
appears at all.

Fix whatever comes back, then run it again:

| What it says | What you did | Fix |
|---|---|---|
| `missing_required` | omitted a property the contract requires | send it, or make it optional in the contract |
| `type_mismatch` | sent a string where a number belongs (or vice versa) | correct the emit |
| `enum_violation` | sent a value outside the allowed set | use an allowed value, or widen the contract |
| `host_not_allowed` | the site's host is outside the workspace allowlist | the owner adds the domain on `/account` |
| `no_contract` | the event name has no contract yet | go back to step 4 |
| `vital_implausible` | a Web Vital outside real-visitor range | nothing to fix, the door caught bad measurement |

## 6. Report back

Tell the owner, in this order:

1. What you instrumented, and which product moments you chose, and why.
2. The real numbers you read back in step 5, with the row counts from the receipt.
3. Anything still refused or ungoverned, and what it needs.
4. What you could not measure and would need their decision on.

Do not claim it works. Show the number you read back.

---

## Connect yourself to Proof over MCP

So you can ask without curl, in this and every later session:

```json
{
  "mcpServers": {
    "proof": {
      "type": "http",
      "url": "https://proof.dorsr.com/api/mcp",
      "headers": { "Authorization": "Bearer AGENT_KEY" }
    }
  }
}
```

Four tools: `list_metrics` (what is answerable), `ask` (plain English, returns a
receipt), `emit_event` (log your own deploys onto the same timeline as the metrics
they move), `propose_event` (step 4 without curl).

## Log your own deploys

An agent's deploy is an event like any other, and putting it on the same timeline
as the traffic makes "did my change break something" answerable:

```bash
curl -s -X POST https://proof.dorsr.com/api/collect \
  -H "content-type: application/json" \
  -d '{"write_key":"WRITE_KEY","events":[{
        "name":"agent.action.deploy","source":"agent",
        "external_id":"deploy-9b1f04d",
        "event_time":"2026-07-28T13:00:00Z",
        "properties":{"target":"myapp.com","commit":"9b1f04d",
                      "outcome":"success","actor":"agent:your-name"}}]}'
```

`external_id` is the idempotency key: re-running the same deploy step records it
once, not twice.

## Privacy defaults you are inheriting

First-party only, no third-party requests. No fingerprinting: a random id in a
first-party cookie. Id-looking path segments are scrubbed before storage. Click
text is masked under `data-proof-mask`. Coarse country only.

Kill switch, honoured by the tag: `window.proofConsent = false`, or the cookie
`proof-consent=denied`.

Do not send personal data. The contract has a `pii` flag for properties that need
one, but the right answer is almost always not to send it.
