# Project export: echooo

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: OpenAI Build Week
- Tagline: Just talk, just snap — "echooo" helps turn it all into memories you can find again.
- Devpost: https://devpost.com/software/echooo-izhalk
- GitHub: https://github.com/hok-io/personal-memory-app
- Demo: https://35-229-162-38.sslip.io/
- Video: https://www.youtube.com/embed/OmIW7unSQ-M?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 1 GitHub contributor(s) — hok-io (40 commits)

## Devpost submission (written by the team)

### Inspiration

I have always wanted to document my life, but I could never find a method that truly suited me. I don’t enjoy posting Stories on social media, and I don’t want every piece of my life to be shared publicly. But when I choose not to post, my photos, thoughts, voice notes, and life moments end up scattered across albums, chat histories, and notes. Even if I once saved them, after some time it becomes difficult to recall what actually happened. The real problem is not a lack of things worth recording, but the high cost of organizing memories. To properly document an experience, I would need to write it out, add context, tag people, categorize topics, and store it in the right place. This process takes time. For someone with a full-time job and a fast-paced life, this kind of effort is often postponed, and many meaningful moments are never preserved. Most existing tools help us store information, but very few truly reduce the cost of organizing our lives. That is why we built echooo: to allow people who don’t want to share publicly, and don’t have time to organize, to still capture their lives in a natural and low-effort way.

### What it does

echooo is a private, voice-first space where users can naturally capture life stories, thoughts, and things they want to do in the future. Its goal is to reduce the cost from “wanting to record something” to “being able to find it again later.” Users don’t need to think about titles, categories, or formats in advance, nor do they need to sit down and write a formal journal entry. At any moment, if they recall an experience, a feeling, or something they want to do, they can simply speak it out and optionally attach a photo. Recording does not require immediate organization. The content is first saved as a pending record, and later transformed into an editable draft. Users can review and confirm people, events, topics, stories, emotions, and follow-up actions, and then decide what becomes part of their memory. The process is: Speak a story, optionally with a photo. Save the raw content as a pending record. Convert it into an editable draft. Review and modify people, topics, events, emotions, and follow-ups. Confirm and save it as a personal memory. Retrieve it later through natural voice queries or filtering. When users want to find something again, they don’t need to remember exact titles, dates, or keywords. They only need to recall fragments that humans naturally remember—who they were with, how they felt, roughly when it happened, or what it was about. echooo doesn’t just store content. It gradually turns scattered life fragments into a personal resource that can be revisited, searched, and used over time.

### How we built it

Before implementation, we spent significant time on product and technical decisions. We clearly defined the problem, the full user flow, and the data and privacy constraints the system must follow. We then used AI to help analyze requirements and compare possible technical approaches. However, we did not rely entirely on AI for decisions. We prioritized frameworks we understood and trusted, ensuring we could validate system behavior and implementation quality even while using AI to accelerate development. The frontend is built with React and Next.js, with a mobile-first experience. Recording is handled directly in the browser using the standard MediaRecorder API, so no extensions or native apps are required. The backend uses Rust and Axum, handling authentication, data ownership isolation, media validation, external service requests, and deterministic search. PostgreSQL stores application state and limited-size media, managed through SeaORM and versioned migrations. In the content processing pipeline: Deepgram Nova-3 converts audio into text. GPT-5.6 transforms transcripts into structured drafts for user review. GPT-5.6 also converts natural language queries into strict, typed search filters. Models never access the database directly or generate SQL. All structured outputs are validated by the Rust backend before executing user-scoped PostgreSQL queries. Photos are not processed by external AI systems. Codex was involved in most of the development process, but not for generating isolated code snippets. Instead, we defined each task as a complete, long-running unit that delivers a usable product slice. Each task includes: The user problem it solves Current system and code state Scope of the task Constraints that must not be broken A clear definition of completion Required tests and validations Documentation and follow-ups Through this approach, Codex contributed not only to code generation but also to full-stack implementation across frontend, backend, database, testing, and deployment. Docker and Docker Compose provide a reproducible self-hosted environment, and the judging version is deployed on an HTTPS server.

### Challenges we ran into

Our biggest constraint was time. Since I have a full-time job, I could only work on echooo during limited hours after work. Turning an idea with many unclear parts into a usable prototype under these conditions was the main challenge. We adopted a strategy of using AI wherever it could accelerate progress. APIs, tools, and subscriptions were introduced whenever they improved productivity, and work was distributed across parallel threads covering product design, frontend, backend, database, testing, and deployment. However, AI being able to implement does not mean implementation problems disappear. The real challenge was enabling AI to complete long-running tasks that span multiple files and system boundaries without drifting away from the original requirements. As tasks grow longer, AI may lose earlier constraints, misunderstand existing code, produce outputs that seem correct but don’t actually work, or break other parts of the system while fixing one issue. This shifted our focus from simply “building features” to: Translating product requirements into tasks AI can reliably execute Defining clear starting and completion conditions Keeping parallel development threads consistent Detecting issues through testing and real usage Using AI to review and fix outputs generated by other AI processes Ensuring long tasks can resume after interruptions We gradually built systems such as project state tracking, task lists, session handoffs, validation checkpoints, and browser-based flow testing, so that each piece of work produces verifiable results rather than just code that appears complete. Beyond development time, the product itself presented key challenges. First, how to help organize personal memories without letting the system decide what is true. echooo separates raw records, structured drafts, and user-confirmed memories, ensuring only the user determines what becomes part of their history. Second, how to safely handle natural language search. Since user queries can be vague, the model is restricted to producing strictly defined filters. The backend rejects invalid outputs and executes only deterministic, user-scoped queries. Third, how to provide a reliable recording experience in mobile browsers, including handling permission denial, interruptions, size and time limits, retries, draft editing, and recovery from failures. The biggest engineering challenge was not just using AI to build quickly, but creating a development process where AI-generated work can be continuously validated, corrected, and extended.

### Accomplishments we're proud of

What we are most proud of is that, within a very limited amount of time, we turned a problem we had repeatedly experienced into a product that actually works. echooo did not begin with a perfectly defined specification. It started from a personal frustration: wanting to preserve meaningful parts of life, but rarely having the time or motivation to organize them. Many parts of the solution were still unclear when development began. As we built the product, the problem gradually became more concrete. What initially seemed like several separate issues—capturing thoughts, organizing memories, tracking things we want to do, and finding old records—came together into one connected experience. The result is a complete working loop: capture → organize → review → confirm → retrieve Users can record a story, turn it into a structured and editable draft, decide what should become part of their confirmed history, and later retrieve it using the incomplete clues people naturally remember. Some parts of the current implementation are still prototype-stage workarounds, but the core experience genuinely functions. It is not only a concept, mockup, or prerecorded demonstration. For us, the most meaningful accomplishment was proving that this personal pain point could become a usable product—and completing that product within the time constraints of the hackathon.

### What we learned

Building echooo changed how we think about both the product problem and the software development process. From a product perspective, we learned that the problem is larger than simply transcribing voice notes or searching old records. People already have many ways to capture information. The real difficulty is the effort required to turn scattered fragments into something meaningful, organized, and retrievable. When capture, organization, confirmation, and retrieval are connected in one flow, those fragments can gradually become a personal information system rather than another collection of forgotten files. We also learned that organization should not require users to fully understand the importance of a moment when they first record it. People should be able to capture something quickly and decide how it fits into their lives later. From an engineering perspective, this was the first time in a long while that we had completed such an intensive product sprint within a short period. The capabilities of current AI development tools are significantly stronger than what we had previously experienced. They can contribute not only to isolated pieces of code, but also to longer tasks involving multiple files, system boundaries, tests, migrations, and browser behavior. This changed the skills we needed to focus on. The challenge was no longer only writing every line manually. It became equally important to describe the problem clearly, choose appropriate tools, define task boundaries, establish completion criteria, validate results, and preserve enough project context for work to continue reliably. For a time-constrained sprint like this, failing to make full use of AI-assisted development would mean giving up a substantial productivity advantage. However, increased implementation speed also makes human judgment more important. Product direction, architecture, privacy boundaries, quality standards, and final verification still need clear human ownership. Most importantly, we learned that personal technology should reduce the effort required to understand and preserve our lives—not create another system that demands constant maintenance.

### What's next

The current direction of echooo is still largely based on our own experiences and assumptions. The most important next step is to understand whether the product fits naturally into other people’s lives. We have already deployed a working version and invited a small group of friends to try it. Based on their experience, we plan to complete one or two rounds of iteration before deciding on the shape of a broader release. The first round of testing will focus on several fundamental questions: Will people naturally use voice to capture personal moments over time? Is the review and confirmation process lightweight enough? Which parts of a memory are useful to structure, and which should remain as an open narrative? What clues do people actually use when trying to retrieve something later? Does echooo feel like a helpful private space, or another tool that requires maintenance? Data ownership and storage will also be an important part of the next stage. Personal memories are highly sensitive, so we want to explore stronger encryption, clearer export and deletion controls, local or user-controlled storage, and self-hosted options. The long-term goal is for users to have meaningful control over where their memories live and how they are processed. Beyond individual memories, echooo could gradually build longer-term personal context. Future versions may help users follow unfinished intentions, recurring topics, changing relationships, and ideas that develop across multiple entries. We also want to make recaps more meaningful. The current prototype can summarize relatively simple sets of records, but future versions could connect memories across a week, month, or longer period—identifying recurring people, emotions, themes, and changes over time. The goal is not to generate more summaries for users to read. It is to help them notice patterns they might otherwise miss, resurface unfinished things at useful moments, and turn scattered records into a clearer understanding of their own lives.

## README (from the GitHub repository)

# echooo

**Seek what you speak — privately.**

echooo is an English-first, voice-first personal memory prototype for OpenAI Build Week. A capture remains pending until the user asks AI to organize it, reviews the proposed structure, and explicitly confirms what becomes memory.

The runnable foundation, account access, authenticated pending text/voice/photo capture, organization boundary, recoverable candidate review, explicit memory commitment, persistent confirmed memory detail, protected title correction, explicit memory/media deletion, zero-typing retrieval, and bounded spoken retrieval are implemented. Every ordinary signed-in screen uses one centered, phone-width Ink & Paper shell with **Home**, **New**, **Pending**, and **Search** bookmark navigation, a fixed echooo footer, and account controls in the avatar popover. New uses one composer for recording/upload, text, and a photo that unlocks only after voice or text is staged. Administrators see only one admission-only **Manage** page. Deepgram Nova-3 and GPT-5.6 adapters remain disabled by default; bounded fictional live fixtures validated each provider independently.

## Accepted stack

- Next.js 16 with React 19, plain JavaScript/JSX, App Router, and static export
- Rust 2024 with Axum on Tokio
- SeaORM 1.1.20 and SeaORM Migration
- PostgreSQL 18; no SQLite fallback
- One same-origin runtime: Axum serves `/api` and the built frontend
- PostgreSQL `media_assets` metadata plus separate `media_blobs(content bytea)` storage
- Docker Compose as the reproducible local/self-host path

The first milestone uses Deepgram Nova-3 Monolingual for bounded English transcription and exact model `gpt-5.6-luna` for structured memory extraction and spoken-search interpretation. Saving a pending capture does not call either service, and all live-provider paths remain disabled by default.

## Repository layout

```text
api/                 Rust application, SeaORM entities, release-baseline migration, and data commands
web/                 Next.js static frontend and focused component tests
contracts/           Versioned JSON schemas and shared fictional organization/search fixtures
scripts/             Local application and fictional-data smoke helpers
docs/                Durable product, architecture, decision, task, and handoff context
compose.yaml          Axum + PostgreSQL local/self-host runtime
Dockerfile            Reproducible multi-stage application image
.dockerignore          Excludes local dependencies, build output, secrets, and private data
```

## Prerequisites

- Docker Engine or Docker Desktop with Compose for the clean-checkout path; no host Node.js, Rust, or PostgreSQL installation is needed.
- For host-based development only: Node.js 24/npm 11, Rust 1.97 or another compatible stable toolchain with `rustfmt` and `clippy`, and PostgreSQL 18.

After installing Rust with rustup, ensure Cargo is on the shell path, for example with `source "$HOME/.cargo/env"`.

## Environment setup

Copy the template and replace the PostgreSQL placeholder locally:

```sh
cp .env.example .env
```

Never commit `.env`. The application validates these variables at startup:

| Variable                                            | Required  | Purpose                                                                                               |
| --------------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------- |
| `APP_ENV`                                           | No        | `development` by default; `production` enforces secure session cookies.                               |
| `APP_HOST`                                          | No        | Listen address; defaults to `127.0.0.1`.                                                              |
| `APP_PORT`                                          | No        | Listen port; defaults to `3000`.                                                                      |
| `APP_PUBLISHED_PORT`                                | Compose   | Localhost port published by Compose; defaults to `3000`.                                              |
| `FRONTEND_DIR`                                      | No        | Static export directory; defaults to `web/out`.                                                       |
| `DATABASE_URL`                                      | Yes       | PostgreSQL connection URL.                                                                            |
| `DATABASE_STARTUP_MODE`                             | No        | `required` normally; `lazy` is reserved for the no-database smoke check.                              |
| `RUN_MIGRATIONS`                                    | No        | Applies SeaORM migrations on startup when `true`; defaults to `true`.                                 |
| `RUST_LOG`                                          | No        | Rust log filter. Logs must not contain personal content.                                              |
| `BOOTSTRAP_ADMIN_LOGIN`                             | Seed      | Normalized login for the fictional bootstrap Admin.                                                   |
| `BOOTSTRAP_ADMIN_PASSWORD`                          | Seed      | Admin password supplied only through local/environment-managed configuration; 12–128 bytes.           |
| `DEMO_USER_LOGIN`                                   | Seed      | Normalized login for the fictional pre-created user.                                                  |
| `DEMO_USER_PASSWORD`                                | Seed      | Demo password supplied only through local/environment-managed configuration; 12–128 bytes.            |
| `DEMO_TIMEZONE_OFFSET_MINUTES`                      | Demo data | Local-day offset for the 14-day River fixture; `-840..840`, defaults to `480` (+08:00).               |
| `SESSION_TTL_SECONDS`                               | No        | Hard lifetime for each opaque session; defaults to 43,200 seconds.                                    |
| `SESSION_ROTATION_SECONDS`                          | No        | Authenticated-use age that triggers token replacement; defaults to 3,600 seconds.                     |
| `SESSION_COOKIE_SECURE`                             | No        | `false` for local HTTP; defaults to and is mandatory as `true` in production.                         |
| `POSTGRES_DB`, `POSTGRES_USER`, `POSTGRES_PASSWORD` | Compose   | PostgreSQL container configuration.                                                                   |
| `ORGANIZATION_LIVE_PROVIDERS_ENABLED`               | No        | Master live-provider switch; defaults to `false`. Credentials alone never enable calls.               |
| `ORGANIZATION_EXTERNAL_PROCESSING_CONFIG_ACK`       | Live only | Must be `deepgram_audio_openai_text_images_excluded.v1` after reviewing that boundary.                |
| `DEEPGRAM_API_KEY`, `DEEPGRAM_MODEL`                | Live only | Server-only key and accepted `nova-3` model; never exposed to the browser.                            |
| `DEEPGRAM_MIP_OPT_OUT`                              | Live only | Explicit `true`/`false` Deepgram model-improvement choice because it can affect processing and price. |
| `OPENAI_API_KEY`, `OPENAI_MODEL`                    | Live only | Server-only key and exact accepted `gpt-5.6-luna` model.                                              |
| `OPENAI_REASONING_EFFORT`, `OPENAI_SERVICE_TIER`    | Live only | Explicit reasoning and service-tier choices; there is no live default.                                |
| `OPENAI_MAX_OUTPUT_TOKENS`                          | Live only | Hard per-extraction output ceiling between 512 and 32,768 tokens.                                     |
| `ORGANIZATION_REQUEST_TIMEOUT_MS`                   | Live only | Whole-request provider timeout between 1,000 and 120,000 ms.                                          |
| `ORGANIZATION_CO

[README truncated for size]

## Detected evidence (automated analysis)

Indexed codebase: 103 recognized source files, 1421 KB.
- CSS (language) — detected in the code
- JavaScript (language) — detected in the code
- Next.js (technology) — detected in the code
- React (technology) — detected in the code
- Rust (language) — detected in the code
- OpenAI (technology) — claimed on Devpost, not found in the code
- PostgreSQL (technology) — claimed on Devpost, not found in the code
- AI coding agent: Codex — evidence: config files committed to the repository

## Codebase structure (from repository index)

### Files (120 of 147)

```
.agents/skills/project-intake/agents/openai.yaml
.agents/skills/project-intake/SKILL.md
.dockerignore
.env.example
.gitignore
AGENTS.md
api/Cargo.lock
api/Cargo.toml
api/src/auth.rs
api/src/bin/admin.rs
api/src/bin/demo.rs
api/src/bin/migrate.rs
api/src/bin/reset.rs
api/src/bin/river_fixture.rs
api/src/bin/seed.rs
api/src/bin/verify_demo_state.rs
api/src/bootstrap.rs
api/src/capture.rs
api/src/config.rs
api/src/db.rs
api/src/demo_storyline.rs
api/src/entities/account_credential.rs
api/src/entities/capture.rs
api/src/entities/event_person.rs
api/src/entities/event.rs
api/src/entities/follow_up.rs
api/src/entities/media_asset.rs
api/src/entities/media_blob.rs
api/src/entities/memory_media.rs
api/src/entities/memory_person.rs
api/src/entities/memory_topic.rs
api/src/entities/memory.rs
api/src/entities/mod.rs
api/src/entities/organization_run.rs
api/src/entities/person.rs
api/src/entities/review_draft.rs
api/src/entities/session.rs
api/src/entities/story_person.rs
api/src/entities/story.rs
api/src/entities/topic.rs
api/src/entities/user.rs
api/src/lib.rs
api/src/main.rs
api/src/media.rs
api/src/memory_controls.rs
api/src/migration/m20260718_000001_foundation.rs
api/src/migration/m20260718_000002_account_access.rs
api/src/migration/m20260718_000003_pending_captures.rs
api/src/migration/m20260718_000004_capture_media.rs
api/src/migration/m20260718_000005_organization_runs.rs
api/src/migration/m20260719_000006_review_and_memories.rs
api/src/migration/m20260719_000007_search_indexes.rs
api/src/migration/m20260719_000008_confirmed_controls.rs
api/src/migration/m20260720_000001_release_baseline.rs
api/src/migration/m20260720_000002_memory_mood_and_presets.rs
api/src/migration/mod.rs
api/src/mood_track.rs
api/src/organization_config.rs
api/src/organization_contract.rs
api/src/organization_provider.rs
api/src/organization.rs
api/src/presets.rs
api/src/recap.rs
api/src/review.rs
api/src/river_fixture.rs
api/src/search.rs
api/src/security.rs
api/src/spoken_search.rs
api/tests/account_access.rs
api/tests/demo_state.rs
api/tests/media_capture.rs
api/tests/mood_track.rs
api/tests/organization.rs
api/tests/provision_accounts.rs
api/tests/recap.rs
api/tests/release_baseline.rs
api/tests/review_commitment.rs
api/tests/river_fixture.rs
api/tests/search_memories.rs
api/tests/spoken_search.rs
compose.yaml
contracts/history/v1/memory-detail.fixture.json
contracts/history/v1/memory-detail.schema.json
contracts/history/v2/confirmed-controls.fixtures.json
contracts/history/v2/confirmed-controls.schema.json
contracts/history/v2/memory-detail.fixture.json
contracts/history/v2/memory-detail.schema.json
contracts/history/v3/confirmed-controls.fixtures.json
contracts/history/v3/confirmed-controls.schema.json
contracts/history/v3/memory-detail.fixture.json
contracts/history/v3/memory-detail.schema.json
contracts/history/v3/memory-options.schema.json
contracts/organization/v1/extraction-candidates.fixture.json
contracts/organization/v1/extraction-candidates.schema.json
contracts/organization/v1/transcription-result.fixture.json
contracts/organization/v1/transcription-result.schema.json
contracts/review/v1/person-name-availability.fixture.json
contracts/review/v1/person-name-availability.schema.json
contracts/review/v2/review-confirmation.fixture.json
contracts/review/v2/review-confirmation.schema.json
contracts/search/v1/search-memories.invalid.fixtures.json
contracts/search/v1/search-memories.schema.json
contracts/search/v1/search-memories.valid.fixture.json
contracts/search/v1/spoken-search-interpretation.fixture.json
contracts/search/v1/spoken-search-transcript.fixture.json
contracts/search/v1/spoken-search.schema.json
contracts/search/v2/search-memories.invalid.fixtures.json
contracts/search/v2/search-memories.schema.json
contracts/search/v2/search-memories.valid.fixture.json
contracts/search/v2/spoken-search-interpretation.fixture.json
contracts/search/v2/spoken-search-transcript.fixture.json
contracts/search/v2/spoken-search.schema.json
Dockerfile
docs/ARCHITECTURE.md
docs/COMPETITION.md
docs/DECISIONS.md
docs/DEMO_RUNBOOK.md
docs/DOCKER_RUNBOOK.md
docs/PRODUCT.md
docs/PROJECT_BRIEF.md
[27 more files omitted for size]
```

### Dependencies

- api/Cargo.toml: anyhow@1, argon2@0.5, axum@0.8, base64@0.22, chrono@0.4, cookie@0.18, dotenvy@0.15, image@0.25, rand_core@0.6, reqwest@0.13.4, sea-orm@=1.1.20, sea-orm-migration@=1.1.20, serde@1, serde_json@1, sha2@0.10, tokio@1, tower@0.5, tower-http@0.6, tracing@0.1, tracing-subscriber@0.3, uuid@1
- web/package.json: @testing-library/jest-dom@6.9.1, @testing-library/react@16.3.2, @vitejs/plugin-react@6.0.3, eslint@9.39.5, eslint-config-next@16.2.10, jsdom@29.1.1, next@16.2.10, prettier@3.9.5, react@19.2.7, react-dom@19.2.7, vitest@4.1.10

### Recent commits (newest first)

- Record public repository verification
- Prepare public Build Week release
- Enrich fictional demo timeline
- Align Memory River readability
- feat: synchronize Tides and Memory River
- feat: add pull-only memory recap
- feat: upgrade Home timeline to sliding Tides window
- Improve River count readability
- Add Home Memory River demo timeline
- Add Home mood timeline
- Use English Search date inputs
- Remove demo-only account notices
- docs: audit migrations and clean Docker workflow
- Implement echooo final approved UI
- feat: add personal presets and memory mood
- Consolidate release deployment and migration baseline
- Complete account tooling and progressive UX
- Complete M1-08 MVP proof and handoff
- Complete M1-07 privacy and security audit
- Complete M1-06B spoken retrieval

## Key source files (fetched from GitHub, selected and truncated for size)

### SECURITY.md

```markdown
# Security Policy

## Supported version

Only the current default branch is supported for this prototype. This project is not a production-grade security service.

## Reporting a vulnerability

Do not open a public issue containing a vulnerability, credential, private memory, recording, image, database export, or other sensitive data. Use GitHub private vulnerability reporting when it is available for this repository, or contact the repository owner through the private contact method listed on their GitHub profile.

Include the affected version, a concise reproduction, impact, and a minimal fictional proof of concept. Never test against another person's account or data. Do not perform destructive testing or expose live secrets.

## Public demo boundary

The judging environment is restricted to fictional, non-sensitive content. Live-provider credentials remain server-side and must never be committed, placed in issues, or sent to the browser.

```

### TERMS.md

```markdown
# Prototype Terms

Last updated: July 22, 2026

echooo is an experimental OpenAI Build Week prototype provided for evaluation, demonstration, and self-hosted exploration. It is not a production service and is not intended for emergency, medical, legal, financial, employment, or other high-stakes use.

Use only information that you have the right and consent to process. The public judging environment is for fictional, non-sensitive content only. Do not upload confidential information, real personal recordings, unlawful content, or material that infringes another person's privacy, publicity, copyright, trademark, or other rights.

Live transcription and AI features may send bounded audio to Deepgram and transcript or text to OpenAI GPT-5.6. Images are excluded from external AI processing in this prototype. Provider availability, output, retention, terms, and charges are outside the software author's control. AI-generated drafts may be incomplete or wrong and remain unconfirmed until a user reviews and confirms them.

The software is licensed under the MIT License and is provided without warranty, as stated in [LICENSE](LICENSE). Availability, durability, fitness for a particular purpose, and uninterrupted access are not guaranteed. Self-hosters are responsible for deployment security, backups, credentials, provider accounts, costs, notices, and legal compliance.

```

### Dockerfile

```
FROM node:24-alpine AS web-build
WORKDIR /src/web
COPY web/package.json web/package-lock.json ./
RUN npm ci
COPY web/ ./
RUN npm run build

FROM rust:1-bookworm AS api-build
WORKDIR /src/api
COPY api/ ./
COPY contracts/ /src/contracts/
RUN cargo build --locked --release --bins

FROM debian:bookworm-slim AS runtime
RUN apt-get update \
    && apt-get install --yes --no-install-recommends ca-certificates curl \
    && rm -rf /var/lib/apt/lists/* \
    && groupadd --system personal-memory \
    && useradd --system --gid personal-memory --no-create-home --home-dir /nonexistent personal-memory
WORKDIR /app
COPY --from=api-build /src/api/target/release/personal-memory-api /usr/local/bin/personal-memory-api
COPY --from=api-build /src/api/target/release/migrate /usr/local/bin/migrate
COPY --from=api-build /src/api/target/release/reset /usr/local/bin/reset
COPY --from=api-build /src/api/target/release/seed /usr/local/bin/seed
COPY --from=api-build /src/api/target/release/verify_demo_state /usr/local/bin/verify_demo_state
COPY --from=api-build /src/api/target/release/admin /usr/local/bin/admin
COPY --from=api-build /src/api/target/release/demo /usr/local/bin/demo
COPY --from=api-build /src/api/target/release/river_fixture /usr/local/bin/river_fixture
COPY --from=web-build /src/web/out /app/frontend
ENV APP_HOST=0.0.0.0 \
    APP_PORT=3000 \
    APP_ENV=production \
    DATABASE_STARTUP_MODE=required \
    FRONTEND_DIR=/app/frontend \
    RUN_MIGRATIONS=true \
    SESSION_TTL_SECONDS=43200 \
    SESSION_ROTATION_SECONDS=3600
USER personal-memory:personal-memory
EXPOSE 3000
CMD ["personal-memory-api"]

```

### web/package.json

```
{
  "name": "personal-memory-web",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "lint": "eslint .",
    "format": "prettier --write .",
    "format:check": "prettier --check .",
    "test": "vitest run",
    "test:watch": "vitest"
  },
  "dependencies": {
    "next": "16.2.10",
    "react": "19.2.7",
    "react-dom": "19.2.7"
  },
  "devDependencies": {
    "@testing-library/jest-dom": "6.9.1",
    "@testing-library/react": "16.3.2",
    "@vitejs/plugin-react": "6.0.3",
    "eslint": "9.39.5",
    "eslint-config-next": "16.2.10",
    "jsdom": "29.1.1",
    "prettier": "3.9.5",
    "vitest": "4.1.10"
  },
  "overrides": {
    "postcss": "8.5.19"
  }
}

```

### api/Cargo.toml

```
[package]
name = "personal-memory-api"
version = "0.1.0"
edition = "2024"
publish = false

[dependencies]
anyhow = "1"
argon2 = "0.5"
axum = { version = "0.8", features = ["multipart"] }
base64 = "0.22"
chrono = "0.4"
cookie = "0.18"
dotenvy = "0.15"
image = { version = "0.25", default-features = false, features = ["jpeg", "png", "webp"] }
rand_core = { version = "0.6", features = ["getrandom"] }
reqwest = { version = "0.13.4", default-features = false, features = ["json", "query", "rustls"] }
sea-orm = { version = "=1.1.20", default-features = false, features = ["macros", "runtime-tokio-rustls", "sqlx-postgres", "with-chrono", "with-uuid"] }
sea-orm-migration = { version = "=1.1.20", default-features = false, features = ["runtime-tokio-rustls", "sqlx-postgres"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
sha2 = "0.10"
tokio = { version = "1", features = ["macros", "net", "rt-multi-thread", "signal", "time"] }
tower-http = { version = "0.6", features = ["fs"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
uuid = { version = "1", features = ["serde"] }

[dev-dependencies]
tower = { version = "0.5", features = ["util"] }

```

### web/app/layout.jsx

```javascript
import "./globals.css";

export const metadata = {
  title: "echooo",
  description: "Seek what you speak — privately.",
};

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}

```

### api/src/main.rs

```rust
use std::net::SocketAddr;

use anyhow::{Context, Result};
use personal_memory_api::{
    build_app_with_providers, config::Config, db, organization_provider::ProviderSet,
};
use tokio::net::TcpListener;
use tracing::info;
use tracing_subscriber::EnvFilter;

#[tokio::main]
async fn main() -> Result<()> {
    dotenvy::dotenv().ok();
    tracing_subscriber::fmt()
        .with_env_filter(
            EnvFilter::try_from_default_env()
                .unwrap_or_else(|_| EnvFilter::new("personal_memory_api=info,tower_http=info")),
        )
        .init();

    let config = Config::from_env()?;
    let providers = ProviderSet::from_config(&config.organization)?;
    let connection = db::connect(&config).await?;
    let address = SocketAddr::new(config.app_host, config.app_port);
    let listener = TcpListener::bind(address)
        .await
        .with_context(|| format!("failed to bind application listener at {address}"))?;
    info!(%address, "application listening");

    axum::serve(
        listener,
        build_app_with_providers(connection, config.frontend_dir, config.auth, providers)
            .into_make_service_with_connect_info::<SocketAddr>(),
    )
    .with_graceful_shutdown(shutdown_signal())
    .await
    .context("application server stopped unexpectedly")?;

    Ok(())
}

async fn shutdown_signal() {
    if let Err(error) = tokio::signal::ctrl_c().await {
        tracing::warn!(%error, "failed to install shutdown signal handler");
    }
}

```

### compose.yaml

```yaml
name: personal-memory-app

x-app-image: &app-image
  image: personal-memory-app:local
  build:
    context: .
    dockerfile: Dockerfile
  init: true
  read_only: true
  tmpfs:
    - /tmp:rw,noexec,nosuid,nodev,size=16m
  cap_drop:
    - ALL
  security_opt:
    - no-new-privileges:true

x-app-environment: &app-environment
  APP_ENV: ${APP_ENV:-development}
  APP_HOST: 0.0.0.0
  APP_PORT: 3000
  DATABASE_URL: postgres://${POSTGRES_USER:-personal_memory}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-personal_memory}
  DATABASE_STARTUP_MODE: required
  FRONTEND_DIR: /app/frontend
  RUN_MIGRATIONS: "true"
  RUST_LOG: personal_memory_api=info
  BOOTSTRAP_ADMIN_LOGIN: ${BOOTSTRAP_ADMIN_LOGIN:?set BOOTSTRAP_ADMIN_LOGIN in .env}
  BOOTSTRAP_ADMIN_PASSWORD: ${BOOTSTRAP_ADMIN_PASSWORD:?set BOOTSTRAP_ADMIN_PASSWORD in .env}
  DEMO_USER_LOGIN: ${DEMO_USER_LOGIN:?set DEMO_USER_LOGIN in .env}
  DEMO_USER_PASSWORD: ${DEMO_USER_PASSWORD:?set DEMO_USER_PASSWORD in .env}
  DEMO_TIMEZONE_OFFSET_MINUTES: ${DEMO_TIMEZONE_OFFSET_MINUTES:-480}
  SESSION_TTL_SECONDS: ${SESSION_TTL_SECONDS:-43200}
  SESSION_ROTATION_SECONDS: ${SESSION_ROTATION_SECONDS:-3600}
  SESSION_COOKIE_SECURE: ${SESSION_COOKIE_SECURE:-false}
  ORGANIZATION_LIVE_PROVIDERS_ENABLED: ${ORGANIZATION_LIVE_PROVIDERS_ENABLED:-false}
  ORGANIZATION_EXTERNAL_PROCESSING_CONFIG_ACK: ${ORGANIZATION_EXTERNAL_PROCESSING_CONFIG_ACK:-}
  DEEPGRAM_API_KEY: ${DEEPGRAM_API_KEY:-}
  DEEPGRAM_MODEL: ${DEEPGRAM_MODEL:-}
  DEEPGRAM_MIP_OPT_OUT: ${DEEPGRAM_MIP_OPT_OUT:-}
  OPENAI_API_KEY: ${OPENAI_API_KEY:-}
  OPENAI_MODEL: ${OPENAI_MODEL:-}
  OPENAI_REASONING_EFFORT: ${OPENAI_REASONING_EFFORT:-}
  OPENAI_SERVICE_TIER: ${OPENAI_SERVICE_TIER:-}
  OPENAI_MAX_OUTPUT_TOKENS: ${OPENAI_MAX_OUTPUT_TOKENS:-}
  ORGANIZATION_REQUEST_TIMEOUT_MS: ${ORGANIZATION_REQUEST_TIMEOUT_MS:-}
  ORGANIZATION_CONNECT_TIMEOUT_MS: ${ORGANIZATION_CONNECT_TIMEOUT_MS:-}
  ORGANIZATION_MAX_RETRIES: ${ORGANIZATION_MAX_RETRIES:-}
  ORGANIZATION_RETRY_BASE_DELAY_MS: ${ORGANIZATION_RETRY_BASE_DELAY_MS:-}
  ORGANIZATION_RATE_LIMIT_PER_MINUTE: ${ORGANIZATION_RATE_LIMIT_PER_MINUTE:-}
  ORGANIZATION_MAX_RUNS_PER_DAY: ${ORGANIZATION_MAX_RUNS_PER_DAY:-}
  ORGANIZATION_MAX_AUDIO_MS_PER_DAY: ${ORGANIZATION_MAX_AUDIO_MS_PER_DAY:-}

services:
  db:
    image: postgres:18-alpine
    environment:
      POSTGRES_DB: ${POSTGRES_DB:-personal_memory}
      POSTGRES_USER: ${POSTGRES_USER:-personal_memory}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}
    healthcheck:
      test:
        - CMD-SHELL
        - pg_isready -U "$${POSTGRES_USER}" -d "$${POSTGRES_DB}"
      interval: 2s
      timeout: 3s
      retries: 20
    volumes:
      - postgres-data:/var/lib/postgresql

  setup:
    <<: *app-image
    depends_on:
      db:
        condition: service_healthy
    environment: *app-environment
    command:
      - /bin/sh
      - -c
      - migrate && seed
    restart: "no"

  app:
    <<: *app-image
    depends_on:
      setup:
        condition: service_completed_successfully
    environment: *app-environment
    healthcheck:
      test:
        - CMD
        - curl
        - --fail
        - --silent
        - http://127.0.0.1:3000/api/health/ready
      interval: 5s
      timeout: 3s
      retries: 20
    ports:
      - "127.0.0.1:${APP_PUBLISHED_PORT:-3000}:3000"

volumes:
  postgres-data:

```

### scripts/seed-fictional.sh

```shell
#!/bin/sh
set -eu

repository_root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
if [ -f "$repository_root/.env" ]; then
  set -a
  # shellcheck disable=SC1091
  . "$repository_root/.env"
  set +a
fi

: "${DATABASE_URL:?DATABASE_URL must be set in .env or the environment}"
cargo run --locked --manifest-path "$repository_root/api/Cargo.toml" --bin seed

```

### scripts/reset-fictional.sh

```shell
#!/bin/sh
set -eu

repository_root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
if [ -f "$repository_root/.env" ]; then
  set -a
  # shellcheck disable=SC1091
  . "$repository_root/.env"
  set +a
fi

: "${DATABASE_URL:?DATABASE_URL must be set in .env or the environment}"
cargo run --locked --manifest-path "$repository_root/api/Cargo.toml" --bin reset

```

[88 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]