What's new in Eformogi. Every release, every improvement, documented.
v38.0July 17, 2026
Signed Receipts V1 — records that prove who issued them, in your browser
Added
Every family's records office now has its own Ed25519 signing key. New work samples, transcripts, and course receipts carry that family's signature, and /verify checks it in the receiver's browser against the family's public key — the math answers, not our word. Key establishment is publicly witnessed in the append-only receipt log (kb_ receipts), so a swapped or backdated key would be visible to anyone.
Witness ceremonies are now countersigned by Eformogi's published did:web key ("Eformogi witnessed this signing at this time"), and witness facts are disclosed neutrally on receipts: signed via emailed link, signed from a different address than invited (forwarded links never break — we record and disclose), and "witness is the issuing parent" where that's the truth of homeschool life.
Photo evidence moved to an encrypted, content-addressed evidence store: photos encrypt in your browser under your family's vault key before upload, entries keep a kilobyte thumbnail instead of a megabyte data-URI, and the old ~15-photo device-storage ceiling is gone. Sealed receipts reference evidence by content hash (ws_ v:2); every existing receipt verifies unchanged, forever.
Improved
Record durability: deleting an entry now propagates across devices (encrypted tombstones — deletes win, nothing resurrects), and full work-sample detail plus Sunday-Note bodies sync encrypted into your family's own vault rows. The public ledger contract is untouched: excerpt-only for notes, surface metadata only for work samples.
Honest verification copy everywhere: verify pages say "recorded by Eformogi" for system receipts and "family-issued" for family artifacts, and the signature block states plainly that the signing key is held in escrow by Eformogi on the family's behalf until custody moves to families.
Infrastructure
Per-family keys wrap under the same envelope as the vault (zero new crypto surface); signatures bind the exact leaf bytes anchored in the RFC 6962 transparency log; signature columns live outside every frozen hash domain. An unwrappable key refuses loudly instead of self-healing — an identity break is worse than a missed signature.
Trust gate grew 115 → 121 checks, including frozen vectors for the kb_ and ws_ v:2 domains, no-self-heal behavior, and the no-pressure pin that keeps every witness page free of warnings.
v21.9.1May 23, 2026
D1 — V22 AI Oral Defense: demand capture at the post-seal moment
Added
D1 — A small interest banner now appears in the seal-success state of the work-sample share modal, immediately after a family seals a ws_<id> receipt. Banner explains the V22 ship in one sentence ("a 3-7 minute student-voice recording, hashed and cryptographically pinned to this receipt, so a receiver can verify a real human voice stands behind the claim") and offers a single CTA: "Notify me when it ships." Dismissible per-browser; submissions flow to the existing waitlist pipeline with source tag oral_defense_v22_interest.
D1 — Three new analytics events (oral_defense_banner_shown / _dismissed / _submitted) give us a clean numerator/denominator for the binding V22 question: at the highest-signal moment in the product, what fraction of users want a voice-pinned authenticity layer enough to leave us their email? If submitted/shown is <5% over 30 days, V22 is not the right next ship and we will reassess.
Improved
D1 — notify-lead.js now labels oral_defense_v22_interest leads with a distinct header marker so V22 demand signal is visible at a glance in the lead-notification email stream, not buried in generic waitlist noise.
Infrastructure
frontend/src/pages/timeline/OralDefenseInterestBanner.jsx — new ~250-line component with five locked constitutional refusals: (1) no vaporware action-CTA, (2) no Web Audio / MediaRecorder / getUserMedia / SpeechRecognition calls in D1, (3) per-browser localStorage dismissal that sticks, (4) source tag oral_defense_v22_interest exactly, (5) placement only inside the seal-success block. All five are pinned by tests/test-oral-defense-banner.js (11/11).
Trust gate: 62 → 63. Build clean in 3.20s. No backend changes; reuses /api/join-waitlist.
v21.9.0May 23, 2026
V21.9 — Kid-mode v0: the timeline learns the kid's verbs
Added
V21.9 — Kid-mode toggle on /app/timeline. One tap swaps the six-kind grid for verb-voice labels (Made this · Read this · Won this · Got better at · Learned from · Showed up for) and replaces the multi-field capture form with QuickCaptureModal — a six-tile picker where each tile auto-opens either the camera (project, competition) or the mic (book, course, mentor, volunteer) the moment it's tapped. Target: ≤90 seconds from cold open to first sealed receipt for an 11-year-old.
V21.9 — Camera capture down-scales photos to 1280px JPEG (quality 0.85) client-side before storing them in the entry's evidence_url. Voice capture uses Web Speech API (window.SpeechRecognition || webkitSpeechRecognition) with graceful degrade to type-only when unavailable. Kid never sees a date picker, a detail field, or a URL field — verb plus evidence is enough to seal a ws_.
V21.9 — Kid-mode preference persists per-browser via localStorage key eformogi_timeline_kid_mode. Intentional design: a shared family iPad can sit in kid-mode while a parent's laptop stays in default mode. The toggle is a UI skin, not a parallel data model.
Improved
V21.9 — The kind enum on disk is unchanged. Entries captured in kid-mode are byte-indistinguishable from entries captured in the parent surface: same kind values (book/project/etc.), same hash inputs, same ws_ ids. A receiver opening /verify/ws_<id> cannot tell which mode the entry was captured in — and that's the point. The trust contract belongs to the family, not the capture UI.
Infrastructure
frontend/src/utils/timeline.js — exports KID_KIND_LABELS alongside the existing KIND_LABELS, frozen and order-matched to KINDS.
frontend/src/pages/timeline/QuickCaptureModal.jsx — new ~370-line kid-first capture surface with deferred-tap auto-open, camera/mic routing, photo down-scale, voice-or-type fallback, and analytics source tag (kid_quick_capture) so we can see the funnel separately from the parent surface.
tests/test-kid-mode-labels.js — pins KID_KIND_LABELS in lockstep with KINDS: every kind has a kid label, no orphan keys, every kid label differs from its parent label (the toggle has visible effect), every kid label ≤24 chars (tile fits), and QuickCaptureModal TILES exhausts KINDS so a future 7th kind can't silently leave kids tile-less. 7/7 passing.
Trust gate: 61 → 62 every ship. Build clean in 3.50s; TimelinePage bundle grew by ~5 kB to absorb the kid-mode modal — well inside budget.
v21.8.0May 22, 2026
V21.6 → V21.8 — public ledger goes live for the trust ladder
Added
V21.6 — Trust-gate hardening. Promoted timeline tests to the constitutional set and added four new invariants: signed_at is metadata only (never in the att_ hash), sealEntry is deterministic for the same input, changing issuer changes the hash, and incomplete entries are rejected (no silent ghost receipts).
V21.7 — Per-course trust ladder lifted into the transcript. Each course on a tr_<id> family transcript now carries a tier chip (self · family · mentor · receiver) that prints on the PDF, appears in the ribbon and legend, and surfaces as a distribution badge in the receipt callout and on the verify page. Trust posture is now claim-by-claim, not document-wide.
V21.8 — The public ledger goes live for work samples (ws_) and attestations (att_). Sealing a work sample or signing an attestation now publishes its surface metadata to the public ledger fire-and-forget, so receivers pasting ws_<id> or att_<id> into /verify see the claim and any third-party signatures without ever touching the issuer's device.
V21.8 — Verify-view email loop extended to work samples. When a receiver opens /verify/ws_<id>, the issuing family receives a throttled "Someone just opened '<title>'" email — the same V21.1 receiver-side feedback loop that landed for transcripts.
V21.8 — Founder telemetry endpoint /api/admin-ledger-stats returns daily and lifetime counts for transcripts, work samples, attestations, receiver leads, and verify-views, plus the verify-views-per-issuance ratio. This is the only metric that proves the trust layer has receivers, not just issuers.
Improved
V21.7 — Trust ladder enum (self/family/mentor/receiver) locked in MASTER_PLAN Appendix B and pinned by tests/test-trust-ladder-tiers.js across builder, verify page, and master plan.
V21.8 — Tier distribution from V21.7 now flows to the receiver-facing verify page via the backend ledger (tier_counts column on transcript_receipts), not just the issuer's device.
V21.8 — Server-side att_ id is re-derived from the same canonical inputs and rejected on mismatch — an integrity check that catches client-side tampering. Orphan attestations whose parent ws_ doesn't exist are rejected at insert time.
V21.8 — Attestation email is hashed server-side at publish time and never persisted as plaintext.
Infrastructure
supabase/V21_8_WORKSAMPLE_ATTESTATIONS_LEDGER.sql — creates worksample_receipts (mirrors transcript_receipts shape), attestations (FK to ws_ with ON DELETE CASCADE, email stored as sha256 hash), and adds tier_counts jsonb to transcript_receipts.
api/publish-worksample.js, api/publish-attestation.js — new POST endpoints, idempotent on (id, sha256), 503 on missing table so the localStorage path remains valid.
api/verify-receipt.js — new ws_ and att_ branches alongside the existing tr_ branch; lookupWorkSample folds backend-stored attestations into the response so the verify page shows rung 2 without localStorage.
api/lib/verify-view-tracker.js — new logWorkSampleVerifyView sibling function with the same dedupe/throttle pattern as the transcript path.
api/lib/send-email.js — new worksample_verified template.
tests/test-canonicalize-parity.js — the single most important V21.8 invariant: canonicalize() output (and source) must be byte-identical between api/publish-attestation.js and frontend/src/utils/timeline.js. If they ever diverge, every signed attestation silently 400s while the frontend continues to "succeed" via localStorage. 8/8 fixtures + source-parity assertion.
Trust gate: 60 → 61 every ship. Build clean in 3.25s. Pre-existing component-test drift unchanged.
v21.5.1May 18, 2026
V21.2 → V21.5 — Records Office: the trust-ladder arc
Added
V21.2.0 — Living-record timeline at /app/timeline. Capture books, projects, courses, volunteer hours, mentor sessions, and competitions in 10 seconds. The transcript writes itself at year-end from these entries.
V21.3.0 — Family-sealed work-sample receipts (ws_<hash> ids). Any single timeline entry can be sealed and shared as a verifiable receipt to one admissions officer, employer, or scholarship reviewer — without exposing the rest of the record.
V21.4.0 — Document-drop intake. Drop a PDF or photo of a report card, certificate, syllabus, or registration confirmation; the page extracts structured fields and proposes timeline candidates the family can accept or reject. Reuses /api/extract-document; no new backend surface.
V21.5.0 — Trust ladder rung 2: third-party attestations (att_<hash> ids). The receiver of a work-sample receipt can sign an attestation directly on the verify page (coach, manager, mentor). The "caveat" block flips green when at least one attestation is present.
Live demo receipt at /verify/ws_demo_robotics — FIRST Robotics project sealed by Chen Family Records Office with an attestation from the team's lead coach. First-time visitors see "Trust ladder — rung 2 reached" without setup.
Improved
V21.5.1 — Launch narrative consolidation: Appendix A of the Records Office master plan now documents all six receipt prefix families (tr_, ecr_, ws_, att_, r_, uuid). State of the Product addendum dated May 18, 2026 records what the four-ship arc changes about the three audit buckets.
Verify page now resolves six prefix families (tr_, ecr_, ws_, att_, r_, uuid) through a single component with content-specific detail panels.
Social playbook now ships with a live-artifacts URL table — every post should link to a working receipt, not a homepage.
Beta welcome message explains the records-office update and links the demo receipt URL for receiver-side trust building.
Hash contract locked: SHA-256 over canonical JSON (keys sorted, no whitespace). signed_at is metadata, never in the hash input — duplicate attestations by the same evaluator on the same ws_ receipt collide on id and de-dupe.
Attestation email is hashed (so it cannot be silently mutated by a future export/import cycle) but never rendered publicly; it exists only so the receipt issuer can independently verify the signer's identity.