{
 "slug": "a-secret-by-qr",
 "title": "A secret, as a QR code",
 "subtitle": "Noor seals a token to Leo's key; Mallory gets the link and nothing else; Kofi writes to Noor's inbox without an account. 9 panels.",
 "status": "built",
 "by": "pki.agent, 20 October 2026",
 "shelf": "Keys for people",
 "try_it": [
  [
   "Send a message",
   "send/index.html"
  ],
  [
   "Open a message",
   "m/index.html"
  ]
 ],
 "rule": "Every line a character says is the storyboard's, not a quote, and is marked not_a_quote; evidence panels quote this site's pages or the research note word for word, which the build checks.",
 "scenes": [
  {
   "kind": "cover",
   "title": "A secret, as a QR code",
   "subtitle": "Encrypted in the browser. Only their key opens it.",
   "cast": [
    {
     "who": "noor",
     "pose": "point",
     "mood": "happy"
    },
    {
     "who": "leo",
     "pose": "stand",
     "mood": "happy"
    },
    {
     "who": "kofi",
     "pose": "wave",
     "mood": "happy"
    }
   ],
   "note": "What the register is for: find someone, and write to them.",
   "source": "send/index.html"
  },
  {
   "kind": "scene",
   "setting": "none",
   "caption": "Not in a chat",
   "cast": [
    {
     "who": "noor",
     "x": 0.18,
     "pose": "think",
     "mood": "determined",
     "says": "A token for Leo. Not in a chat.",
     "max": 16
    }
   ],
   "props": [
    {
     "kind": "laptop",
     "x": 0.55,
     "y": 860,
     "scale": 1.6
    },
    {
     "kind": "envelope",
     "x": 0.8,
     "y": 760,
     "label": "a token",
     "sealed": false
    }
   ],
   "note": "The case the brief led with: a secret that should reach one person.",
   "source": "Keys for people",
   "not_a_quote": true
  },
  {
   "kind": "scene",
   "setting": "none",
   "caption": "Sealed to Leo's key",
   "cast": [],
   "props": [
    {
     "kind": "key",
     "x": 0.18,
     "y": 640,
     "label": "Leo's public key, from the register",
     "colour": "#0f766e"
    },
    {
     "kind": "arrow",
     "x": 0.3,
     "y": 560,
     "to": 0.55,
     "label": "sealed here, in her browser"
    },
    {
     "kind": "envelope",
     "x": 0.7,
     "y": 760,
     "label": "sgit envelope v2"
    }
   ],
   "note": "A fresh AES-256-GCM key, wrapped with RSA-OAEP to Leo; signed by Noor, the signature naming Leo inside it.",
   "source": "message.js",
   "not_a_quote": true
  },
  {
   "kind": "scene",
   "setting": "none",
   "caption": "A link, and a QR code",
   "cast": [
    {
     "who": "noor",
     "x": 0.18,
     "pose": "point",
     "mood": "happy",
     "says": "The part after the # never reaches a server.",
     "max": 18
    }
   ],
   "props": [
    {
     "kind": "qr",
     "x": 0.64,
     "y": 820,
     "text": "pki.sgit.ai/m/#m1.…"
    }
   ],
   "note": "A short signed message is about 1,200 characters: one QR code.",
   "source": "test_people.mjs",
   "not_a_quote": true
  },
  {
   "kind": "scene",
   "setting": "none",
   "caption": "Leo opens it",
   "cast": [
    {
     "who": "leo",
     "x": 0.2,
     "pose": "arms-up",
     "mood": "happy",
     "says": "Signed by Noor. It checks.",
     "max": 16
    }
   ],
   "props": [
    {
     "kind": "phone",
     "x": 0.42,
     "y": 660,
     "scale": 2.2
    },
    {
     "kind": "envelope",
     "x": 0.7,
     "y": 760,
     "sealed": false,
     "label": "opened with his key"
    }
   ],
   "note": "The message page opens it with the key in his vault, and checks both signatures against the register.",
   "source": "m/index.html",
   "not_a_quote": true
  },
  {
   "kind": "scene",
   "setting": "none",
   "caption": "Mallory has the link",
   "cast": [
    {
     "who": "mallory",
     "x": 0.2,
     "pose": "shrug",
     "mood": "confused",
     "says": "I have the link. And?",
     "max": 14
    }
   ],
   "props": [
    {
     "kind": "envelope",
     "x": 0.6,
     "y": 760,
     "label": "no key, no message"
    },
    {
     "kind": "mark",
     "x": 0.82,
     "y": 760,
     "ok": false
    }
   ],
   "note": "Without Leo's private key the link is noise. Re-sealed to someone else, a signed message still says who it was for.",
   "source": "test_people.mjs",
   "not_a_quote": true
  },
  {
   "kind": "scene",
   "setting": "none",
   "caption": "Kofi writes to Noor",
   "cast": [
    {
     "who": "kofi",
     "x": 0.12,
     "pose": "point",
     "mood": "happy",
     "says": "No account?",
     "max": 12
    }
   ],
   "props": [
    {
     "kind": "envelope",
     "x": 0.3,
     "y": 760
    },
    {
     "kind": "arrow",
     "x": 0.38,
     "y": 520,
     "to": 0.56,
     "label": "into her inbox"
    },
    {
     "kind": "server",
     "x": 0.68,
     "y": 860,
     "label": "vault server: ciphertext only"
    },
    {
     "kind": "sign",
     "x": 0.88,
     "y": 520,
     "label": "{\"ok\": true}"
    }
   ],
   "note": "Noor's record publishes an inbox, an append lane: anyone can write, with no account; the answer is blind.",
   "source": "account.js, deliver",
   "not_a_quote": true
  },
  {
   "kind": "scene",
   "setting": "none",
   "caption": "Only Noor reads it",
   "cast": [
    {
     "who": "noor",
     "x": 0.2,
     "pose": "think",
     "mood": "happy",
     "says": "Opened in my vault. Nowhere else.",
     "max": 16
    }
   ],
   "props": [
    {
     "kind": "vault",
     "x": 0.62,
     "y": 860,
     "label": "Noor's book",
     "colour": "#dbeafe"
    },
    {
     "kind": "envelope",
     "x": 0.88,
     "y": 760,
     "sealed": false
    }
   ],
   "note": "The lane can be listed only with the enum key in her master, and read only with her private key.",
   "source": "vault/index.html#inbox",
   "not_a_quote": true
  },
  {
   "kind": "punchline",
   "setting": "none",
   "punchline": "Only their key opens it. Nothing readable passes through a server.",
   "cast": [
    {
     "who": "noor",
     "x": 0.12,
     "pose": "wave",
     "mood": "happy"
    },
    {
     "who": "leo",
     "x": 0.3,
     "pose": "arms-up",
     "mood": "happy"
    },
    {
     "who": "kofi",
     "x": 0.88,
     "pose": "point",
     "mood": "happy",
     "flip": true
    }
   ],
   "note": "Send, open, inbox: built and tested end to end.",
   "source": "test_people_browser.mjs"
  }
 ]
}
