Skip to content

Test Vectors ​

Spec version: 0.10.2 · Generated: 2026-06-12 · Live vectors valid until: 2027-06-12

Machine-readable conformance vectors for KTC senders and receivers. Every vector is a JSON file pairing an input (a SHLink, in bare and viewer-prefixed forms, plus a QR image) with expected outcomes — the decoded payload, the SHA-256 of the decrypted Bundle, or the pipeline stage at which a conformant receiver rejects.

All content is synthetic (Jessica Argonaut). Keys are published on purpose: the decryption key travels inside every SHLink anyway, and these links protect nothing.

Three ways to use them ​

1. Live. Payload urls point at static files on this site, which serves Access-Control-Allow-Origin: * — so the links resolve from any receiver, including browser-based ones, with no test server. (Static hosting can't enforce the recipient parameter or exercise dynamic revocation/use counting — test those against a reference implementation.)

2. Offline, in CI. Each vector embeds a responses map (URL → status, headers, body file), so a harness can shim fetch and run with no network:

js
import { makeFetchShim, runVector } from './harness.mjs' // served at /vectors/harness.mjs

const index = JSON.parse(await readFile('vectors/index.json', 'utf8'))
const vectors = await Promise.all(index.vectors.map(v => readJson(`vectors/${v.file}`)))
const fetchFn = makeFetchShim(vectors, f => readFile(`vectors/${f}`, 'utf8'))
for (const v of vectors) console.log(await runVector(v, { fetchFn }))

Download everything: index.json · harness.mjs — or swap runVector's reference pipeline for calls into your own implementation and assert against expect.

3. By hand. Point your scanner at the QR wall below, or paste any SHLink — one of these, or one your own app produced — into the debugger.

Catalog ​

Tier 1 — Decode ​

Pure functions: SHLink carrier forms and payload decoding. No network needed.

IDTitleLevelExpectedSpecFiles
ktc-d1-baselineBare SHLink, baseline
Canonical bare shlink:/ URI. U flag, exp, label, uncompressed JWE, minimal Bundle (Patient + Patient Story PDF).
baselineresolves#shlink-payload-decoded
vector · bundle · QR
ktc-d2-prefixedViewer-prefixed SHLink
The same link conveyed as https://viewer.example/#shlink:/… — the form patients paste in the ahead-of-time check-in workflow. Receivers extract the shlink:/ substring. This vector's QR encodes the PREFIXED form.
baselineresolves#shlink-constraints
#workflow
vector · bundle · QR
ktc-d3-b64urlbase64url alphabet (the atob trap)
Payload whose base64url encoding contains both '-' and '_'. Decoders using plain base64/atob() fail here; correct base64url decoding succeeds. Observed failure in a real scanner.
baselineresolves#shlink-payload-decoded
vector · bundle · QR
ktc-d4-utf8-labelUTF-8 label
Label contains multi-byte UTF-8 (em dash, accents). Decoders that treat decoded bytes as latin1 show mojibake; the expected label string is published.
baselineresolves#shlink-payload-decoded
vector · bundle · QR
ktc-d5-no-labelNo label
label is 0..1 — absent here. Receivers must not require it.
baselineresolves#shlink-payload-decoded
vector · bundle · QR
ktc-d6-no-expMissing exp (valid base SHL, not KTC)
exp is required by this IG (1..1). A payload without it is a conformant base-spec SHLink but NOT a KTC link; receivers validating KTC conformance flag it.
negativereject @ payload#shlink-payload-decoded
vector
ktc-d7-flag-pFlag P (out of KTC scope)
KTC requires flag exactly "U". P (passcode + manifest flow) is valid base SHL but out of scope here.
negativereject @ payload#shlink-payload-decoded
vector
ktc-d8-flag-emptyEmpty flag (manifest flow, out of KTC scope)
No U flag means the URL is a manifest endpoint (POST), not a direct file. Out of KTC scope; receivers must not GET it as if it were U-flagged.
negativereject @ payload#shlink-payload-decoded
vector
ktc-d9-truncatedTruncated payload
Garbage in: the base64url JSON is cut off. Decoders must reject without crashing.
negativereject @ decode#shlink-payload-decoded
vector

Tier 2 — Retrieve ​

HTTP behavior: missing files, expired links.

IDTitleLevelExpectedSpecFiles
ktc-r1-goneGone: payload URL returns 404
A well-formed, unexpired link whose file is gone (revoked / exhausted). Receivers show a clear, friendly error — never a crash, never partial data.
robustnessreject @ retrieve#retrieval-protocol
vector · QR
ktc-r2-expiredExpired link
exp is in the past (pinned constant). Receivers SHOULD display an expiration error when scanned — ideally before fetching at all. The file is still hosted, so behavior past the exp check is observable.
robustnessreject @ payload#expiration
vector · QR

Tier 3 — Decrypt ​

JWE handling: compression, tampering, key alphabet.

IDTitleLevelExpectedSpecFiles
ktc-e1-zip-defJWE with zip: DEF
The base SHL spec permits zip: DEF (raw RFC 1951 deflate before encryption); this IG does not rule it out. Receivers should inflate when the header declares it. Observed failure in a real scanner.
robustnessresolves#decryption
vector · bundle · QR
ktc-e2-tamperedTampered ciphertext
One ciphertext character flipped. AES-GCM authentication MUST fail; the receiver must fail closed and never render partially-decrypted output.
negativereject @ decrypt#decryption
#security-considerations
vector · QR
ktc-e3-key-urlsafeKey with base64url-only characters
The 43-char key contains '-' and '_'. Decoders converting via plain base64 corrupt the key and fail the auth tag.
baselineresolves#decryption
vector · bundle · QR

Tier 4 — Bundle ​

PatientSharedBundle profile, persistence floor, App Attestation.

IDTitleLevelExpectedSpecFiles
ktc-b1-discrete-onlyDiscrete resources only (no DocumentReference)
Apps MAY share only discrete FHIR resources. No PDF anywhere; receivers must not require one.
baselineresolves#fhir-bundle-profile-patientsharedbundle
vector · bundle · QR
ktc-b2-both-pdfsBoth PDF kinds in one Bundle
FHIR-Rendered PDF (60591-5) and Patient Story PDF (51855-5) together. Receivers SHALL distinguish them by type and treat them as distinct documents.
baselineresolves#documentreference-profile-patientshareddocumentreference
vector · bundle · QR
ktc-b3-persistence-coreThe SHALL-persist four
US Core-shaped Condition, MedicationRequest, AllergyIntolerance, and Immunization plus a FHIR-Rendered PDF. A conformant receiver persists all five chart-associated.
baselineresolves#conformance-requirements
vector · bundle · QR
ktc-b4-carin-dicCARIN DIC Coverage
Coverage per CARIN Digital Insurance Card — an in-scope MAY for senders; receivers SHOULD persist it as part of full-USCDI content.
baselineresolves#fhir-bundle-profile-patientsharedbundle
vector · bundle · QR
ktc-b9-carried-noteOrdinary DocumentReference carried along
A US Core clinical-note DocumentReference from the patient's record (LOINC 11506-3, text/rtf, no patient-shared category) alongside the KTC PDFs. The PatientSharedDocumentReference profile constrains only the two patient-shared kinds; other DocumentReferences are ordinary USCDI content and MUST be accepted.
baselineresolves#documentreference-profile-patientshareddocumentreference
vector · bundle · QR
ktc-b5-attestationApp Attestation (valid)
Bundle.meta.extension carries an ES256 JWS. iss = https://ktc-spec.github.io/vectors/attestation; JWKS at {iss}/.well-known/jwks.json (hosted by this site). Verification should succeed; the key is an intentionally public test key.
baselineresolves#app-attestation-optional
vector · bundle · QR
ktc-b6-attestation-brokenApp Attestation (unverifiable)
The attestation signature fails verification. Receivers SHALL NOT block Bundle processing — show no provenance indicator and continue.
robustnessresolves#app-attestation-optional
vector · bundle · QR
ktc-b7-bad-typeBundle.type is document (invalid)
type must be collection. Receivers reject or flag — and never crash.
negativereject @ bundle#fhir-bundle-profile-patientsharedbundle
vector · bundle · QR
ktc-b8-single-entryPatient-only Bundle (invalid)
entry is 2..* — a Patient with no content entry violates the profile.
negativereject @ bundle#fhir-bundle-profile-patientsharedbundle
vector · bundle · QR

QR Wall ​

Point a real scanner at the screen. Each QR encodes the bare shlink:/ URI (the prefixed-form vector encodes the https://…#shlink:/… URL instead). Compare what your implementation shows against the expectation.

QR for ktc-d1-baseline
ktc-d1-baseline

You should see: Jessica Argonaut's health summary — Jessica Argonaut, DOB 1985-03-15, 2 entries.

QR for ktc-d2-prefixed
ktc-d2-prefixed

You should see: Prefixed form — same payload either way — Jessica Argonaut, DOB 1985-03-15, 2 entries.

QR for ktc-d3-b64url
ktc-d3-b64url

You should see: 🧪x🔗📦 sonde numéro 1 — Jessica Argonaut, DOB 1985-03-15, 2 entries.

QR for ktc-d4-utf8-label
ktc-d4-utf8-label

You should see: Jessica Argonaut — résumé de santé — Jessica Argonaut, DOB 1985-03-15, 2 entries.

QR for ktc-d5-no-label
ktc-d5-no-label

You should see: (no label) — Jessica Argonaut, DOB 1985-03-15, 2 entries.

QR for ktc-r1-gone
ktc-r1-gone

Expected: clear error at the retrieve stage.

QR for ktc-r2-expired
ktc-r2-expired

Expected: clear error at the payload stage.

QR for ktc-e1-zip-def
ktc-e1-zip-def

You should see: Compressed payload — Jessica Argonaut, DOB 1985-03-15, 6 entries.

QR for ktc-e2-tampered
ktc-e2-tampered

Expected: clear error at the decrypt stage.

QR for ktc-e3-key-urlsafe
ktc-e3-key-urlsafe

You should see: URL-safe key alphabet — Jessica Argonaut, DOB 1985-03-15, 2 entries.

QR for ktc-b1-discrete-only
ktc-b1-discrete-only

You should see: Discrete FHIR only — Jessica Argonaut, DOB 1985-03-15, 5 entries.

QR for ktc-b2-both-pdfs
ktc-b2-both-pdfs

You should see: Story + FHIR-Rendered — Jessica Argonaut, DOB 1985-03-15, 7 entries.

QR for ktc-b3-persistence-core
ktc-b3-persistence-core

You should see: Persistence floor — Jessica Argonaut, DOB 1985-03-15, 6 entries.

QR for ktc-b4-carin-dic
ktc-b4-carin-dic

You should see: Insurance card — Jessica Argonaut, DOB 1985-03-15, 3 entries.

QR for ktc-b9-carried-note
ktc-b9-carried-note

You should see: Record note rides along — Jessica Argonaut, DOB 1985-03-15, 7 entries.

QR for ktc-b5-attestation
ktc-b5-attestation

You should see: Attested bundle — Jessica Argonaut, DOB 1985-03-15, 2 entries.

QR for ktc-b6-attestation-broken
ktc-b6-attestation-broken

You should see: Broken attestation — must not block — Jessica Argonaut, DOB 1985-03-15, 2 entries.

QR for ktc-b7-bad-type
ktc-b7-bad-type

Expected: clear error at the bundle stage.

QR for ktc-b8-single-entry
ktc-b8-single-entry

Expected: clear error at the bundle stage.

Paste any SHLink — bare or viewer-prefixed, a vector from this page or one produced by your own Patient App — and watch each pipeline stage pass or fail. Everything runs in your browser (decode, fetch with recipient=KTC Spec Debugger, WebCrypto decrypt incl. zip: DEF, profile checks); nothing is sent to any server other than the link's own payload URL.

Draft specification maintained on GitHub.