Skip to content

Memreda/memreda

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

34 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Memreda

Memreda is a low-friction handover inbox for one family sharing care:

voice note or forwarded email β†’ editable handover β†’ private family timeline β†’ daily brief

It preserves continuity from messy channels people already use, with source-backed handovers and clear ownership of what must carry forward. It is intentionally not medical advice, an emergency service, medical records, medication management, surveillance, a generic family dashboard, or a chatbot.

 voice note ─┐
 typed note ─┼─▢ immutable source  ─▢  cautious AI handover  ─▢  private timeline  ─▢  daily brief
forwarded    β”‚   (audio/text kept        (edit before it            (source-linked         (Resend,
email        β”˜    verbatim, never          ever counts as             continuity:            every send
                   overwritten)             saved)                    what changed,          logged)
                                                                       carry-forward,
                                                                       tags, replies)

Every arrow above is a real API boundary in server.js, not aspirational: a source is written once and never mutated, the AI call only ever drafts the three editable handover fields (never a diagnosis, never invented detail), and nothing reaches the shared timeline without a human confirming it first.

Start a private family circle The Today view for a circle Writing a source note Reviewing the editable handover The private timeline

Start a circle β†’ share an update β†’ review the editable handover β†’ the private timeline. Live screenshots from a fresh instance, not mockups.

Customer-zero scope

This Phase 1 app supports one private family circle per signed-in owner in the UI:

  • Passwordless owner sign-in using expiring email magic links.
  • A private circle and email/link invitation for contributors.
  • Browser-recorded or uploaded audio voice notes.
  • Immutable stored source metadata and transcript, separately from AI output and later edits.
  • OpenAI transcription plus cautious, schema-validated editable fields: What happened, Worth remembering, and Tomorrow.
  • Private membership-checked timeline, edit, soft-delete, and JSON export.
  • Validated basic-auth inbound email endpoint for Postmark-style forwarding. It only accepts known circle members, deduplicates by MessageID, and creates an owner-reviewable draft before any email-derived handover can enter the timeline.
  • Daily owner digest script via Resend, with every delivery attempt recorded.

The incoming-email and digest transport patterns were informed by /opt/verasettle-repos/verasettle-email-agent; no payout, settlement, finance, or product logic was copied.

Phase 2 continuity layer

Phase 2 adds a deliberately narrow private continuity layer on top of saved handovers:

  • What changed? presents conservative family-reported New, Continuing, Resolved, and Needs follow-up items since a member last opened that view. Every item opens its source-backed handover; it does not diagnose or make clinical conclusions.
  • Related handovers gather into lightweight topic threads showing first mention, latest update, source count, and handover evidence.
  • A handover's explicit Tomorrow wording becomes a lightweight carry-forward commitment. Circle members can claim and complete it, and can optionally place it on the small manual/shared action list.
  • The circle action list supports only manual or commitment-derived items, claim, completion, provenance, and timestamps. It has no recurring tasks, calendar, notifications, projects, or priorities.
  • A constrained private-history question searches only the circle's saved handovers and original private source text, then returns a short answer and linked evidence. It is not a general chat assistant.
  • Each voice note, written note, and forwarded email has circle-only chronological replies and a seen acknowledgement. There are no DMs, global chat rooms, public reactions, or nested threads.
  • Members can add or remove private post-hoc tags on sources, replies, and tasks. Tags are annotations; they never alter the original source and can be included in the next digest without creating push notifications.

Continuity extraction is currently deterministic and source-linked, so it remains available while external AI is unavailable. It stores generated continuity records separately from sources and editable handovers.

Local setup

Requirements: Node 20+ and npm.

cp .env.example .env
npm install
npm run migrate
npm run dev

Open http://localhost:3000. Without email credentials, sign-in and invitation responses show one-time development links in the UI. Without an OpenAI key, uploads use a clearly labelled local-development transcript fallback; this is for flow testing only, not a real transcription result.

Environment variables

Variable Purpose
PORT HTTP port, default 3000.
APP_URL Public canonical HTTPS URL used in sign-in and invitation links. It must be a new dedicated hostname, not the existing Tailnet /memory-lane path.
DATABASE_PATH SQLite file path, default ./data/memory-lane.db.
UPLOAD_DIR Private filesystem directory for voice-note files, default ./uploads. Do not expose it as static content.
SESSION_SECRET Reserved for deployment secret management; use a long random value.
OPENAI_API_KEY Enables real OpenAI transcription and handover generation.
OPENAI_TRANSCRIPTION_MODEL Defaults to gpt-4o-mini-transcribe.
OPENAI_HANDOVER_MODEL Defaults to gpt-4.1-mini.
TRANSCRIPTION_API_URL, TRANSCRIPTION_API_KEY, TRANSCRIPTION_MODEL Optional OpenAI-compatible transcription override (for example Groq's /openai/v1/audio/transcriptions with whisper-large-v3). Falls back to the OPENAI_* settings when unset.
HANDOVER_API_URL, HANDOVER_API_KEY, HANDOVER_MODEL Optional OpenAI-compatible chat-completions override for handover drafting. Falls back to the OPENAI_* settings when unset.
RESEND_API_KEY, EMAIL_FROM Enable real magic-link, invitation, and digest delivery.
INBOUND_BASIC_USER, INBOUND_BASIC_PASS Required together to enable the inbound email webhook. Use the credentials in the provider webhook URL/auth header.

Database and migrations

npm run migrate creates a SQLite database and the Phase 1 tables plus Phase 2 continuity tables for member visits, topic evidence, change items, commitments, circle tasks, source replies, seen acknowledgements, member tags, and private-history query evidence. SQLite WAL mode is enabled. Back up the database and the private upload directory together; neither is public static content.

Authentication and privacy

Magic links expire after one hour; invitation links expire after seven days. Session cookies are HTTP-only, same-site, and secure when APP_URL is HTTPS. Every timeline, upload, update, delete, and export operation checks circle membership; only the owner can invite. Inbound email uses constant-time basic-auth comparison, rejects unknown senders without revealing membership, and deduplicates MessageID values.

Use HTTPS, a strong operational secret, private encrypted disk/object storage, backups, deletion/retention procedures, and a real sender/domain validation policy before handling sensitive family material in production.

OpenAI behavior

Audio is sent to OpenAI only when OPENAI_API_KEY is configured. The transcription is stored separately. A second OpenAI request drafts only the three editable handover strings from that transcript. Server-side length/type validation is applied before persistence. The prompt instructs the model not to diagnose, give medical advice, invent details, or turn family-reported wording into a conclusion. Users see that output is editable before saving.

This is the actual function (server.js), not a paraphrase β€” the constraint lives in the prompt sent on every call, and a provider outage degrades to a labelled fallback instead of blocking the family's flow:

async function generateHandover(transcript) {
  if (!HANDOVER_KEY) return { ...fieldsFromText(transcript), provider: 'local-development-fallback', model: null };
  const prompt = `Turn this private family voice-note or email transcript into a cautious editable handover.
Do not diagnose, give medical advice, infer facts, or add details. Preserve family-reported wording.
Return JSON only with strings: happened, remembering, tomorrow. ...`;
  try {
    const response = await fetch(HANDOVER_URL, {
      method: 'POST',
      headers: { authorization: `Bearer ${HANDOVER_KEY}`, 'content-type': 'application/json' },
      body: JSON.stringify({
        model: HANDOVER_MODEL,
        messages: [
          { role: 'system', content: 'You write concise, non-medical, source-faithful family handovers.' },
          { role: 'user', content: prompt },
        ],
        response_format: { type: 'json_object' },
        temperature: 0.2,
      }),
      signal: AbortSignal.timeout(45_000),
    });
    if (!response.ok) return { ...fieldsFromText(transcript), provider: `${providerName(HANDOVER_URL)}-unavailable-fallback`, model: HANDOVER_MODEL };
    const body = await response.json();
    return { ...validateFields(JSON.parse(body.choices?.[0]?.message?.content || '{}')), provider: providerName(HANDOVER_URL), model: HANDOVER_MODEL };
  } catch {
    return { ...fieldsFromText(transcript), provider: `${providerName(HANDOVER_URL)}-unavailable-fallback`, model: HANDOVER_MODEL };
  }
}

Three things worth noticing: the model never sees or touches anything except the transcript it was called with, validateFields re-clamps every string server-side regardless of what the model returns, and a timeout, a non-200, or a malformed JSON body all fall through to the same deterministic, clearly-labelled fallback rather than surfacing a raw provider error to a family member.

Email and daily digest

Configure a Postmark-style provider to POST JSON to POST /api/inbound/email with HTTP basic authentication. The endpoint accepts the provider fields FromFull.Email, MessageID, Subject, and TextBody (or stripped HtmlBody), preserves the source/transcript, and creates an owner-reviewable email draft. Only the owner can edit and publish that draft to the family timeline.

Run the digest once with:

npm run digest

Schedule that command once per day through the host scheduler (for example a cron job or platform scheduled job). It emails the owner a brief of last-24-hour handovers using Resend and records success/failure in digest_deliveries.

Testing

npm test

The integration test exercises owner magic-link sign-in, circle creation, contributor invitation/acceptance, private audio authorization, typed-note source persistence, source-linked continuity, commitments, task claim/completion, source replies/seen acknowledgements, source/reply/task tags, private-history evidence, export, inbound-email deduplication, and outsider denial against a temporary SQLite database. Run it where a temporary localhost listener is permitted.

Deployment

This is one Node process plus a persistent private volume for SQLite/uploads. Production service, digest timer, Caddy hostname snippet, environment template, and an installation guide are in deploy/. The unit listens only on loopback port 9020; Caddy is the HTTPS edge. SQLite and audio are stored under /var/lib/memory-lane, never in a static Caddy root. A production host must provide: a new public HTTPS hostname, APP_URL, persistent encrypted storage, environment secrets, Resend, OpenAI, and a verified inbound-email provider. Start command: npm start; migration command: npm run migrate; daily scheduled command: npm run digest.

Memory Lane is deployed at https://memreda.xyz. The Node process listens only on 127.0.0.1:9020 behind Caddy HTTPS; the former Tailnet /memory-lane preview still points to the old Python static server and is not used by this deployment. Verify the live app with curl -fsS https://memreda.xyz/api/session, then complete the two-person customer-zero flow below before treating Phase 1 as complete.

Live verification steps

  1. Owner requests a magic link at https://memreda.xyz and receives it at the configured real inbox.
  2. Owner creates a circle and sends an invitation to the contributor's real inbox.
  3. Contributor accepts the invite, records a short browser voice note, reviews the OpenAI-generated editable fields, and saves it.
  4. Owner confirms the handover and preserved transcript in the private timeline, edits it, exports it, and receives the scheduled daily brief.
  5. Contributor forwards an email through the configured Postmark inbound address; the owner reviews/edits the resulting email draft before publishing it to the timeline.

Safety boundaries and known limitations

  • Not for emergencies: call local emergency services when someone may be in immediate danger.
  • Not medical advice or a clinical record; verify health decisions with appropriate professionals.
  • One-circle customer-zero UI; no multi-family administration or complex RBAC.
  • Inbound sender validation is secure transport plus membership matching, but production should also enforce provider SPF/DKIM/domain verification.
  • Audio files are local private disk storage in this deployment shape; move to encrypted object storage for multi-instance hosting.
  • Phase 2 continuity records are conservative deterministic summaries rather than clinical conclusions. Review/edit the underlying handover; the product intentionally does not make diagnoses or certify that a family action occurred.
  • Saved voice notes remain private, replayable, and retryable even while a transcription provider is unavailable; the TRANSCRIPTION_* override exists so an OpenAI-compatible fallback provider can restore the live voice β†’ transcript β†’ handover flow without any code change.

How Codex and GPT-5.6 Were Used

Memreda was built in Codex, with GPT-5.6 doing the reasoning and code generation end to end:

  • Product and architecture. GPT-5.6 audited the original static prototype, preserved its warm mobile-first visual direction, and designed the smallest production-grade architecture that could honestly serve one family: a single Node process, SQLite in WAL mode, and a private upload volume β€” no framework, no ORM, no build step.
  • Core implementation. The authentication flow (magic links, sessions, invitations), immutable source capture for voice/typed/email inputs, the transcription β†’ cautious editable-handover pipeline, the Phase 2 continuity layer (What changed?, topic threads, carry-forward commitments, circle tasks, source replies and seen acknowledgements, member tags, evidence-linked private-history questions), the daily owner digest, and the systemd/Caddy deployment files were all implemented through Codex sessions.
  • Testing. The end-to-end customer-zero integration test β€” owner sign-in through invitation, voice and note capture, continuity, commitments, tasks, inbound-email deduplication, outsider denial, and export β€” was written in Codex alongside the features it covers.
  • Primary Codex session (majority of core functionality, 2026-07-18): 019f76c6-3420-7032-be7b-b1c9df2824d4. Product ideation ran in an earlier session the same day (019f7695-93cf-70f2-a369-09934d9ee004).

At runtime the deployed app makes exactly two kinds of model calls, both deliberately constrained: audio transcription, and drafting the three editable handover strings from a transcript (never diagnosis, advice, or invented detail). These default to OpenAI (gpt-4o-mini-transcribe, gpt-4.1-mini) and are OpenAI-compatible-configurable via the TRANSCRIPTION_*/HANDOVER_* variables so a fallback provider can keep a family's flow alive through a quota gap.

About

Private continuity layer for families sharing care

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors