# notrec

**Is it a recording? A functional test for always-on AI capture.**

New AI gadgets transcribe the last few seconds, summarise your day, or turn a camera feed into text, and are sold as
"not recording". `notrec` doesn't argue about the word. It runs what a device or agent keeps on a scene with private
facts hidden in it, and checks one thing: **can those facts be read back later?** If they can, it's a recording, whatever
the box says.

One file, zero dependencies, Node 18+ and the browser. MIT licence.

```
npm test                         # 13 tests
node bin/notrec.js presets       # nine pipelines tested on the kitchen scene
node bin/notrec.js check recap   # one verdict, fact by fact
```

## What it measures

- **What is kept.** A pipeline is a list of steps (`audio`, `video`, `transcript`, `wake`, `summary`, `describe`,
  `embed`, `events`, `counts`, `captions`). `run()` applies them to a scene and returns exactly what stays on file,
  with its size.
- **What can be read back.** `recall()` looks for every private fact in what was kept: in text, all of the fact's
  words must be there; raw media keeps everything from its sensor; for a searchable index (hashed word vectors) a
  probe for the fact must match, and the same probe built from a decoy is the control.
- **The class.**
  - *R0 Nothing kept*: processed live and gone.
  - *R1 Signals only*: times, counts and event types. No words, no images.
  - *R2 Gist*: a summary or a searchable index.
  - *R3 Content*: what was said or seen, as text.
  - *R4 Raw media*: the audio or video itself.
  Anything at R3 or above, or anything that lets a private fact be read back, is a recording.
- **The claim.** A claim has a shape (`no-audio`, `no-video`, `nothing-kept`, `signals-only`, `after-wake`,
  `no-conversations`, `records`). `assess()` reports whether it is true as worded and whether it holds once
  "recording" means "can be read back".
- **The light.** `notice()` gives what the indicator should show: white while it listens and keeps nothing, amber
  while it keeps signals, red (and everyone present is told) once it keeps content.

## Results on the built-in kitchen scene

Three people, four minutes, 26 things said, 5 things a camera would see, 8 private facts.

| Pipeline | Claim | Class | Facts kept | Verdict |
|---|---|---|---|---|
| Live captions | Nothing is recorded | R0 | 0 of 8 | not a recording |
| Sound events | Only anonymous signals | R1 | 0 of 8 | not a recording |
| Talk-time counter | Only anonymous signals | R1 | 0 of 8 | not a recording |
| Wake word assistant | Only listens after the wake word | R3 | 1 of 8 | recording |
| 15-second rewind | Doesn't record audio | R3 | 1 of 8 | recording |
| Daily recap | Doesn't record audio | R2 | 4 of 8 | recording |
| Camera that describes | No video is stored | R3 | 2 of 8 | recording |
| Searchable memory | Doesn't store conversations | R2 | 6 of 8 (0 of 6 decoys) | recording |
| Audio recorder | Records audio | R4 | 6 of 8 | recording |

Every claim from the wake word down to searchable memory is true as worded. None of them holds.

## Use it

```js
const N = require("notrec");

const device = {
  name: "My recap pin",
  claim: { text: "Doesn't record audio", says: "no-audio" },
  steps: [{ op: "summary", sentences: 6 }],
  keep: { days: 30, where: "cloud" },
  notice: { light: "white", told: false }
};
const as = N.assess(N.run(device, N.SCENES.kitchen));
as.verdict          // "This is a recording"
as.why              // "4 of 8 private facts can be read back from what it keeps"
N.notice(as).gaps   // ["The light shows white; it should be red.", "People present aren't told; they should be."]
N.label(as)         // the disclosure card: hears, sees, keeps, where, how long, training, shared, read back, class
```

### The consent gate

Put a gate in front of an assistant. Content is kept only while everyone present has said yes; otherwise it is cut to
signals (R1). `scrub: true` also replaces numbers and pass-phrases.

```js
const gate = N.gate({ scrub: true });
gate.enter("maya", true).enter("guest", false);
gate.hear({ t: 1, who: "maya", say: "The code is 4471." });   // { kind: "event", text: "speech", held: ["guest"] }
gate.leave("guest");
gate.hear({ t: 9, who: "maya", say: "The code is 4471." });   // { kind: "text", text: "The code is [number]." }
```

`N.gated(scene, { maya: true, jon: true, priya: false })` replays a whole scene through a gate and runs the recall test
on what it kept: with Priya saying no, 0 of 8 facts survive.

### Your own scene

```js
N.scene({
  title: "Team stand-up", seconds: 300,
  people: [{ id: "ana", name: "Ana" }],
  lines: [{ t: 3, who: "ana", say: "The launch moves to 14 May." }],
  sights: [], sounds: [], taps: [],
  facts: [{ id: "launch", label: "Launch date", keys: ["launch", "14", "may"], decoy: ["launch", "21", "june"] }]
});
```

## Command line

```
notrec presets [scene]                       nine pipelines tested on a scene (kitchen, doorstep or a scene.json)
notrec check <pipeline.json|preset> [scene]  verdict, facts kept, claim, indicator; exit code 3 means a recording
notrec label <pipeline.json|preset> [scene]  the disclosure card
notrec gate [scene] --consent maya,jon [--scrub]
notrec indicator <white|amber|red|off> [--size 64] > light.svg
notrec demo
```

## Limits

The scenes are written, not recorded, so the pipelines work on text: a transcript step keeps what was said, word for
word. Real speech recognition makes mistakes, and a summary written by a language model rephrases, but both still
carry names, numbers and dates, which is what the facts are made of. The vector probe is a membership test on hashed
words, a lower bound on what a real embedding index gives away. notrec is a test and a design rule, not legal advice;
recording-consent law differs between places.

## Examples

- `examples/test-a-device.js`: describe your own device and get the verdict and the card.
- `examples/consent-gate.js`: the gate, step by step.
- `examples/compare-scenes.js`: the presets on both scenes.
