Inspiration

I started by exploring the AI Passport website, and I loved the idea straight away. Erin and David built it around one question: if AI is going to be personal, why does every app act like it has never met you?

So I asked a smaller version of that. What is the one thing that hurts most when you are forced to repeat it?

The answer I kept coming back to was a medical diagnosis.

To get a disability accommodation, you have to prove you deserve it — not once, but every time, to every institution, starting from zero. And the proof everybody asks for is a diagnosis. An exam board that only needs to know you get 1.5× time will ask for your condition, your medication, and a letter from your clinician. It has no use for any of it. It asks because a letter is the only unit of proof that exists.

The American Bar Association describes this requirement as so onerous that it "adds an element of discrimination to the enforcement of anti-discrimination law."

That is a permission problem, not a paperwork problem. Which makes it a Passport problem.

What it does

Entitle carries what a person is entitled to between institutions, and never the diagnosis behind it.

A clinic assesses someone once and issues a pass. The functional accommodations — extra_time, flexible_start — become named, verifiable fields the person holds. Institutions request exact fields, state a purpose and a duration, and receive only what she grants.

The diagnosis, clinician, medication and report stay sealed in a Vault that is not requestable at any scope. Not gated behind a stronger permission — unaskable. There is no flow that discloses a diagnosis, so there is no flow an employer can pressure a candidate into.

The demo runs the whole lifecycle: request a pass from the clinic, watch the diagnosis get sealed at issue, then decide what three different institutions get. The ledger showing what was and was not disclosed updates live as you edit each grant.

How I built it

The prototype is a single self-contained HTML file — inline styles, inline SVG icons, the logo embedded as a data URI. No build step, no dependencies, no framework. It runs from file://, works offline, and deploys to Vercel with zero configuration.

The interaction is a small state machine over five phases — request, issue, hold, share, receipts — with the ledger derived from state rather than hard-coded, so nothing in the demo is faked.

The 12-slide deck is generated in Python with python-pptx, not built by hand. Every colour, margin and type size is a constant in one script, so a single change propagates across all twelve slides and the grid never drifts. Pillow generates the logo variants.

Testing was done with jsdom and headless Chrome, driving the real page and asserting on computed styles and DOM state.

Challenges I ran into

Text that was invisible and I could not see why. Parts of the deck vanished against the cream background. Rather than eyeball it, I computed WCAG contrast ratios — my secondary grey was 3.09:1 and my accent 4.26:1, both below the 4.5:1 minimum. I rebuilt the palette so every text colour measures ≥4.5:1 and annotated each constant with its ratio. Shipping a deck about disability accommodation with sub-AA contrast would have been an unforced error.

A colour-inheritance bug I would never have found by reading. Values on the passport card rendered black on a dark card. The cause: <button> does not inherit color the way a <div> does, and I had set font-family: inherit but not color: inherit. Fine on white, invisible on dark.

A "broken" button that was never broken. I spent several rounds debugging a button that did nothing. It worked in jsdom, worked in headless Chrome, worked everywhere I tested. The real cause: I was writing test files into the folder Live Server watches, so every save silently reloaded the tab mid-click and reset the demo. The lesson was about my debugging method, not the code — I kept verifying in environments that did not have the problem.

Encoding corruption from my own tooling. A PowerShell step read a UTF-8 file as ANSI and rewrote it, double-encoding every em-dash and multiplication sign — including inside JavaScript strings. I made the whole file pure ASCII with HTML entities so the entire class of bug became impossible.

What I learned

Minimisation that only counts fields is not minimisation. Fields that are individually harmless can be jointly identifying. That reframed the whole design: the interesting question is not how many fields travel, but what can be inferred from the combination.

The strongest privacy claim is structural, not procedural. "We protect your diagnosis" is a promise. "There is no request path that returns it" is a property. The second one cannot be eroded by a policy change or a pushy employer.

The negative space is the product. The most valuable screen in the whole build is the one listing what an institution did not receive — especially the last line: that she asked anyone else. Cross-institution correlation is a real disclosure that no current process even counts as one.

Say what it cannot do. A passport cannot make retention technically impossible; it makes it evidential. Stating that plainly is stronger than overclaiming.

What's next

One corridor, one direction — prove that a single clinical assessment can serve two institutions without either learning a diagnosis. Three screens: issue, request-and-edit, receipt-and-revoke. Useful with three participants, not three hundred.

Out of scope for v1: multi-country assessor accreditation, employer-side enforcement tooling, and write-back of new entitlements. Scope is a feature — the smallest version that proves the claim is the one worth building.

Built With

Share this project:

Updates

Submission history