๐Ÿ›ก๏ธ
Before You Begin
My Legacy Goals is a browser-based vault โ€” it runs entirely on this device with no cloud, no server, and no account required beyond this file.
โš ๏ธ Important: My Legacy Goals cannot recover your vault if you lose access to this device or clear your browser data without a backup. You are solely responsible for keeping your backup file safe.
Learn more at MyLegacyGoals.com  ยท  Full Disclaimer
Welcome Back
Sign in to access your Legacy Vault.
No account? Create one free
๐Ÿ”’ Your account is stored on this device only. No data leaves your browser.
โ† MyLegacyGoals.com
๐Ÿ“„
Legacy Report
Print Unencrypted PDF Summary
โœ…
Sovereign Digital Vault
Your legacy data is encrypted and stored only in this browser.
๐Ÿ‘ฅ Private Mode

Browse Sections

6 Units
๐Ÿ’ฐ
0 Records

Financial

Bank accounts, insurance policies, investments, and debts.

๐Ÿ“„
0 Records

Legal

Wills, trusts, deeds, and power of attorney documents.

๐Ÿ”‘
0 Records

Digital Credentials

Passwords, PINs, and access codes for critical services.

๐ŸŒ
0 Records

Social Media

Social media handles and memorialization preferences.

โค๏ธ
0 Records

Personal Notes

Letters to loved ones, instructions, and final wishes.

๐Ÿฉน
0 Records

Medical

Health history, prescriptions, and provider contacts.

My Life Story

0 Events

Document the moments that shaped your life โ€” milestones, memories, travels, losses, and more โ€” so your heirs can truly know who you were.

No life events yet โ€” add your first memory.
`; html += `

My Legacy Goals โ€” Legacy Report

Generated: ${new Date().toLocaleString()}


`; Object.entries(CATS).forEach(([key,cat]) => { const recs = vault[key]||[]; html += `

${cat.icon} ${cat.label} (${recs.length})

`; if (!recs.length) { html += '

No records.

'; return; } recs.forEach(r => { const vals=rV(r); const files=rF(r); html += '
'; cat.fields.filter(f=>f).forEach((f,fi) => { if (vals[fi]) html += `
${f}
${esc(vals[fi])}
`; }); if (files.length) html += `
Attachments
${files.map(f=>`
${esc(f.name)}
`).join('')}
`; html += '
'; }); }); const lifeEvents = vault.life_events || []; html += `

๐Ÿ“– My Life Story (${lifeEvents.length} event${lifeEvents.length!==1?'s':''})

`; if (!lifeEvents.length) { html += '

No life events recorded.

'; } else { lifeEvents.forEach(ev => { const t = {birth:'๐ŸŽ‚',family:'๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ง',relationship:'๐Ÿ’',education:'๐ŸŽ“',career:'๐Ÿ’ผ',travel:'โœˆ๏ธ',achievement:'๐Ÿ†',home:'๐Ÿ ',health:'โค๏ธโ€๐Ÿฉน',loss:'๐Ÿ•Š๏ธ',faith:'๐Ÿ™',milestone:'๐ŸŒŸ',other:'๐Ÿ“–'}[ev.type]||'๐Ÿ“–'; html += `
${t} ${esc(ev.title||'Untitled')}
`; if (ev.date||ev.location) html += `
${[ev.date,ev.location].filter(Boolean).map(esc).join(' ยท ')}
`; if (ev.people) html += `
People: ${esc(ev.people)}
`; if (ev.story) html += `
${esc(ev.story)}
`; html += '
'; }); } html += ''; return html; } window.generatePDF = function() { const win = window.open('','_blank'); if (!win) { alert('Please allow pop-ups to generate the PDF.'); return; } win.document.write(buildReportHTML(loadVault())); win.document.close(); win.print(); }; window.savePDF = function() { const blob = new Blob([buildReportHTML(loadVault())], {type:'text/html'}); const a = Object.assign(document.createElement('a'), { href:URL.createObjectURL(blob), download:`legacy-report-${new Date().toISOString().slice(0,10)}.html` }); a.click(); URL.revokeObjectURL(a.href); }; // Encrypted file const MAGIC = new Uint8Array([0x4D,0x4C,0x47,0x56,0x31]); const PEPPER = 'mylegacygoals-sovereign-vault-v1'; async function deriveKey(passphrase, salt, usage) { const enc = new TextEncoder(); const raw = await crypto.subtle.importKey('raw', enc.encode(passphrase+PEPPER), 'PBKDF2', false, ['deriveKey']); return crypto.subtle.deriveKey({ name:'PBKDF2', salt, iterations:150000, hash:'SHA-256' }, raw, { name:'AES-GCM', length:256 }, false, [usage]); } window.saveEncryptedVault = function() { showModal(`

๐Ÿ”’ Save Encrypted File

Creates a .mlgvault file. Only readable by this app with your passphrase.
AES-256-GCM ยท PBKDF2 150k iterations ยท Zero cloud.
`); }; window.doSaveEncrypted = async function() { const p1=(document.getElementById('enc-pass')||{}).value||''; const p2=(document.getElementById('enc-pass2')||{}).value||''; const err=document.getElementById('enc-err'); if (!p1){err.textContent='Please enter a passphrase.';err.style.display='block';return;} if (p1!==p2){err.textContent='Passphrases do not match.';err.style.display='block';return;} err.style.display='none'; try { const vault=loadVault(); const salt=crypto.getRandomValues(new Uint8Array(16)); const iv=crypto.getRandomValues(new Uint8Array(12)); const key=await deriveKey(p1,salt,'encrypt'); const cipher=await crypto.subtle.encrypt({name:'AES-GCM',iv},key,new TextEncoder().encode(JSON.stringify(vault))); const out=new Uint8Array(MAGIC.length+16+12+cipher.byteLength); out.set(MAGIC,0); out.set(salt,MAGIC.length); out.set(iv,MAGIC.length+16); out.set(new Uint8Array(cipher),MAGIC.length+28); const blob=new Blob([out],{type:'application/octet-stream'}); const a=Object.assign(document.createElement('a'),{href:URL.createObjectURL(blob),download:'legacy-vault-'+new Date().toISOString().slice(0,10)+'.mlgvault'}); a.click(); URL.revokeObjectURL(a.href); overlay.style.display='none'; } catch(e){err.textContent='Encryption failed: '+e.message;err.style.display='block';} }; window.loadEncryptedVault = function(input) { const file=input.files[0]; input.value=''; if (!file) return; showModal(`

๐Ÿ“ Load Encrypted File

File: ${esc(file.name)}
`); window._pendingVaultFile=file; document.getElementById('dec-pass').addEventListener('keydown',e=>{if(e.key==='Enter')doLoadEncrypted();}); }; window.doLoadEncrypted = async function() { const pass=(document.getElementById('dec-pass')||{}).value||''; const err=document.getElementById('dec-err'); if (!pass){err.textContent='Please enter your passphrase.';err.style.display='block';return;} err.style.display='none'; try { const buf=await window._pendingVaultFile.arrayBuffer(); const bytes=new Uint8Array(buf); for(let i=0;i{}); } else { const url=URL.createObjectURL(blob); const win=window.open(url,'_blank'); if(win){win.onload=()=>{win.print();URL.revokeObjectURL(url);};} else{URL.revokeObjectURL(url);alert('Please allow pop-ups.');} } }; // Life Events const LIFE_TYPES = { birth: { icon:'๐ŸŽ‚', label:'Birth / Origin' }, family: { icon:'๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ง', label:'Family' }, relationship: { icon:'๐Ÿ’', label:'Relationship' }, education: { icon:'๐ŸŽ“', label:'Education' }, career: { icon:'๐Ÿ’ผ', label:'Career' }, travel: { icon:'โœˆ๏ธ', label:'Travel' }, achievement: { icon:'๐Ÿ†', label:'Achievement' }, home: { icon:'๐Ÿ ', label:'Home / Move' }, health: { icon:'โค๏ธโ€๐Ÿฉน', label:'Health' }, loss: { icon:'๐Ÿ•Š๏ธ', label:'Loss / Grief' }, faith: { icon:'๐Ÿ™', label:'Faith / Spiritual' }, milestone: { icon:'๐ŸŒŸ', label:'Milestone' }, other: { icon:'๐Ÿ“–', label:'Other' }, }; function renderLifeTimeline() { const vault = loadVault(); const events = vault.life_events || []; const countEl = document.getElementById('life-count'); if (countEl) countEl.textContent = events.length + ' Event' + (events.length!==1?'s':''); const el = document.getElementById('life-timeline'); if (!el) return; if (!events.length) { el.innerHTML = '
No life events yet โ€” add your first memory.
'; return; } el.innerHTML = '
' + events.map((ev,i) => { const t = LIFE_TYPES[ev.type] || LIFE_TYPES.other; const files = ev.files || []; return `
${t.icon}
${esc(ev.title||'Untitled Event')}
${ev.date?`${esc(ev.date)}`:''} ${esc(t.label)}
${ev.story?`
${esc(ev.story)}
`:''} ${ev.location?`
๐Ÿ“ ${esc(ev.location)}
`:''} ${ev.people?`
๐Ÿ‘ฅ ${esc(ev.people)}
`:''} ${files.length?filesHTML(files,'life_events',i,false):''}
`; }).join('') + '
'; } function lifeEventForm(p) { p = p || {}; return `
`; } window.addLifeEvent = function() { window._stagedFiles = []; showModal(`

๐Ÿ“– Add Life Event

${lifeEventForm()} `); }; window.editLifeEvent = function(idx) { window._stagedFiles = []; const vault = loadVault(); const ev = (vault.life_events||[])[idx]; if (!ev) return; const existingFiles = ev.files||[]; showModal(`

โœ Edit Life Event

${lifeEventForm(ev)} ${existingFiles.length?`
Existing Files
${filesHTML(existingFiles,'life_events',idx,true)}
`:''} `); }; window.saveLifeEvent = function(idx) { const g = id => (document.getElementById(id)||{}).value||''; const ev = { type: g('lf-type')||'other', date: g('lf-date').trim(), title: g('lf-title').trim(), story: g('lf-story').trim(), location: g('lf-location').trim(), people: g('lf-people').trim(), files: [], }; if (!ev.title && !ev.story) { alert('Please add a title or story.'); return; } const vault = loadVault(); if (!vault.life_events) vault.life_events = []; if (idx === -1) { ev.files = window._stagedFiles||[]; vault.life_events.unshift(ev); } else { ev.files = (vault.life_events[idx].files||[]).concat(window._stagedFiles||[]); vault.life_events[idx] = ev; } saveVault(vault); window._stagedFiles=[]; overlay.style.display='none'; renderLifeTimeline(); }; window.deleteLifeEvent = function(idx) { if (!confirm('Delete this life event?')) return; const vault = loadVault(); vault.life_events.splice(idx,1); saveVault(vault); renderLifeTimeline(); }; // Wire category cards document.querySelectorAll('.dash-cat-card').forEach((card,i) => { const key=Object.keys(CATS)[i]; const link=card.querySelector('.dash-cat-link'); if(link){link.onclick=()=>openSegment(key);} }); refreshCounts(); renderLifeTimeline(); // Auto-open from landing page CTA if (new URLSearchParams(location.search).get('start') === 'life') { history.replaceState(null,'','/dashboard'); setTimeout(() => { document.getElementById('life-timeline').scrollIntoView({ behavior:'smooth', block:'center' }); addLifeEvent(); }, 400); } })();