본문으로 건너뛰기
5분 AI.
GITHUB TRENDING GO WEEKLY #7 딥다이브 · 2026-10-23 분석

engram 딥다이브
— AI의 기억을 데이터베이스로 설계하는 법

CLI·MCP 서버

빠른 판단

무엇
버그 원인·설계 결정·프로젝트 규칙을 SQLite·FTS5에 저장하고 MCP·CLI·TUI로 찾는 Go 기억 도구
이럴 때
코딩 에이전트나 세션을 바꿔도 과거 결정·실패 원인·진행 맥락을 검색해 이어서 활용할 때
입력
구조화해 남긴 경험·결정·관례와 검색어
결과
프로젝트·세션을 넘겨 재사용할 기억 검색 결과
대화가 끝나면 잊어버리는 코딩 에이전트에게, 다음 세션에서도 꺼내 쓸 수 있는 기억을 붙이는 도구입니다. Go 실행 파일 안에 SQLite·전문 검색·MCP·HTTP API·터미널 화면을 담고, 필요할 때만 클라우드 복제를 연결합니다. 이 글은 기억 하나가 저장되고, 검색되고, 다른 기기로 전달되는 과정을 따라가며 데이터 정합성과 시스템 경계를 배웁니다. Gentleman-Programming/engram · ⭐7,554 · 포크 789 · Go · MIT · 2026-10-23 GitHub 기준.
목차
  1. 프로젝트 한줄 요약
  2. 왜 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석
  5. 디렉토리 구조 해부
  6. 학습 포인트 (기술별)
  7. 하드웨어 / 시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한줄 요약

에이전트가 고른 경험을, 다음 대화에서 다시 찾게 하는 기억 저장소

engram은 코딩 에이전트가 해결한 버그, 선택한 설계, 프로젝트 관례를 구조화된 메모리로 저장하는 프로그램입니다. 에이전트는 MCP 도구로 기록하고, 사람은 CLI나 TUI로 열람할 수 있습니다. 기본 저장 위치는 ~/.engram/engram.db이며, 핵심 저장 계층은 internal/store입니다.

이 저장소에서 배울 핵심은 AI의 기억도 결국 소유권·중복·검색·재시도를 설계해야 하는 데이터라는 점입니다. 모델의 가중치를 바꾸는 학습이 아니라, 필요한 기록을 다음 대화의 입력에 다시 넣는 방식입니다. 저장된 내용이 있어도 에이전트가 검색해서 읽지 않으면 그 대화에 활용되지 않습니다.

한 문장 비유

매번 새로 출근하는 개발자에게, 검색 가능한 인수인계 수첩을 건넵니다

대화창은 오늘의 회의실이고, SQLite는 퇴근해도 남는 서랍입니다. “로그인 오류를 고쳤다”는 기록에 원인과 수정 파일을 함께 적어 두면, 다음 담당자가 같은 구덩이에 빠질 확률을 줄일 수 있습니다.

수첩이 있다고 내용이 저절로 정확해지지는 않습니다. 무엇을 적을지 고르는 역할은 주로 에이전트에 있고, engram은 그 기록을 보존하고 되찾는 장치를 제공합니다.

용어
관찰 기록 (observation)
한 번의 학습이나 결정을 담는 메모리 단위입니다. Observation에는 제목·본문·종류·프로젝트·세션뿐 아니라 수정 횟수, 중복 횟수, 검토 예정 시점 같은 관리 정보도 있습니다. 대화 전체와 기록 하나를 구분하는 것이 첫 번째 읽기 포인트입니다.

1-1. 기억 하나의 생애

  1. 선별: 에이전트가 다시 쓸 가치가 있는 버그 원인이나 결정을 고릅니다.
  2. 기록: mem_save가 프로젝트를 확인하고 저장 계층을 호출합니다.
  3. 보존: SQLite의 observations와 검색 색인이 함께 갱신됩니다.
  4. 회수: 다음 세션에서 mem_context·mem_search로 후보를 찾습니다.
  5. 상세 조회: mem_get_observation으로 필요한 기록만 자세히 읽습니다.
# 설치된 실행 파일로 사용하는 CLI 예시
engram save '로그인 재시도 원인' \
  'What: 토큰 갱신 경합 수정. Why: 동시 요청이 갱신을 중복 실행. Where: auth/refresh.go. Learned: 갱신을 직렬화한다.' \
  --type bugfix --project study-app --topic auth/refresh-race

engram search '토큰 갱신' --project study-app --match all
engram context --project study-app

cmd/engram/main.go의 parseSaveArgs()에서 제목과 본문은 위치 인자입니다. 토픽 옵션 이름은 --topic입니다. 내부의 topic_key 필드와 CLI 옵션의 이름이 다르므로 문서와 실제 파서를 함께 읽어야 합니다.

학습 목표
작은 저장 API에서 제품 전체의 설계까지 연결하기

처음부터 클라우드를 이해할 필요는 없습니다. cmdSave() → AddObservationWithResult() → SearchContext()를 먼저 읽고, 같은 저장 규칙을 MCP와 HTTP가 어떻게 공유하는지 넓혀 가면 됩니다.

2왜 주목받는가

세션을 넘는 기억이라는 수요와, 설치 부담을 줄인 구현

2-1. 순위보다 먼저 볼 문제

긴 개발 작업에서는 에이전트를 바꾸거나 대화를 압축할 때 이전 결정의 근거가 빠지기 쉽습니다. 같은 프로젝트의 테스트 관례나 실패한 접근을 계속 설명해야 한다면, 기록을 대화 밖으로 분리할 이유가 생깁니다. engram은 이 문제를 특정 모델의 내부 기능 대신 별도 저장소와 공통 도구 인터페이스로 다룹니다.

2026-10-23 GitHub Trending Go Weekly #7, 스타 7,554개라는 수치는 관심의 크기를 보여줍니다. 다만 순위만으로 유입 원인이나 성능 우위를 알 수는 없습니다. 다음 세 가지는 구현에서 읽을 수 있는 매력에 대한 해석입니다.

에이전트와 기억 저장소를 분리하면, 사용하는 에이전트가 바뀌어도 기록의 수명을 유지할 수 있습니다. 단, 도구를 연결하는 것과 에이전트가 적절한 시점에 도구를 쓰는 것은 별개입니다. 저장 규칙을 안내하는 프로토콜과 플러그인도 함께 봐야 합니다.

2-2. 비슷한 접근과 비교하면 무엇이 보이나

접근잘 맞는 상황engram의 장점과 교환 조건
AGENTS.md팀이 합의한 규칙을 코드와 함께 관리경험을 세션별로 쌓고 검색하기 쉽습니다. 대신 정제된 규칙 문서를 대체하지는 않습니다.
대화 요약 파일다음 대화에 적은 분량을 넘기기프로젝트 필터·토픽별 갱신·수정 이력을 제공합니다. 저장 구조와 운영을 배워야 합니다.
임베딩 기반 RAG표현이 달라도 의미가 비슷한 문서 찾기기본 검색에 모델·벡터 서버가 필요 없습니다. 어휘가 다른 동의어를 알아서 연결하는 능력은 제한됩니다.
claude-mem에이전트의 작업 흔적을 기억으로 만드는 인접 제품engram 저자는 에이전트가 고른 요약과 단일 Go 실행 파일을 차별점으로 설명합니다. 경쟁 제품의 현재 기능은 별도 확인이 필요합니다.

마지막 행은 docs/COMPARISON.md에 실린 engram 저자의 비교 관점입니다. 그 문서의 “웹 UI” 행은 engram을 TUI 중심으로만 설명하지만, 현재 소스에는 Cloud 대시보드도 있습니다. 따라서 비교 문서 전체를 최신 경쟁 제품 평가표로 받아들이기보다, 설계 철학을 설명하는 자료로 읽는 편이 정확합니다.

2-3. “의존성 없음”을 정확히 읽기

README의 “single binary, zero dependencies”는 기본 로컬 실행에 별도 Node.js·Python·Docker가 필요 없다는 설명입니다. go.mod에는 SQLite 드라이버, MCP SDK, TUI 라이브러리 등 다수의 빌드 의존성이 있습니다. Cloud 서버는 PostgreSQL을 쓰고, 선택적 의미 비교는 외부 에이전트 CLI를 호출합니다.

비유

완성된 자동차를 운전할 때 부품 공장이 필요 없다는 말과 비슷합니다. 자동차에 부품이 없다는 뜻은 아닙니다. 빌드 의존성, 실행 의존성, 선택 기능의 의존성을 나누면 홍보 문장을 기술적으로 읽을 수 있습니다.

3기술 스택 전체 지도

버전은 커밋 ab461b9의 의존성 파일과 배포 설정 기준

3-1. 백엔드와 데이터 계층

기술실제 선언역할과 읽을 위치
Gogo 1.25.10go.mod의 언어·도구 체인 요구. 모듈 경로는 github.com/Gentleman-Programming/engram/v3.
SQLite 드라이버modernc.org/sqlite v1.58.0C 컴파일러에 의존하지 않는 로컬 DB 접근. internal/store/store.go.
MCP SDKmcp-go v0.44.0도구 스키마·호출 결과·stdio 서버. internal/mcp/mcp.go.
PostgreSQL 드라이버pgx/v5 v5.7.6선택형 Cloud 저장 계층. internal/cloud/cloudstore.
네트워크·문자 처리x/net v0.57.0
x/text v0.40.0
Go 확장 모듈. 함께 선언된 x/sys는 v0.47.0.
설정 파일yaml.v3 v3.0.1YAML 처리. 실제 제품의 JSON·환경변수 설정과 구분해서 봅니다.
HTTP·SQL 기초Go 표준 라이브러리net/http의 ServeMux, database/sql의 연결·트랜잭션을 직접 사용합니다.

v1.58.0은 Go 드라이버 모듈의 버전입니다. SQLite 엔진 버전과 같은 번호라고 읽으면 안 됩니다. FTS5는 이 저장소에서 사용하는 SQLite의 전문 검색 기능이며, 검색을 위해 외부 벡터 데이터베이스를 띄우지 않습니다.

3-2. 프론트엔드는 두 종류다

화면·통합버전·근거구현 방식
터미널 UIbubbletea v1.3.10
bubbles v1.0.0
lipgloss v1.1.0
internal/tui에서 상태·입력 메시지·출력 화면을 분리합니다.
Cloud HTMLtempl v0.3.1001*.templ을 Go 렌더링 코드로 생성합니다. 브라우저 화면도 서버가 만듭니다.
부분 화면 갱신HTMX 2.0.4internal/cloud/dashboard/static/htmx.min.js의 버전 문자열. 저장소에 포함된 정적 파일입니다.
기본 웹 스타일Pico CSS 2.1.0internal/cloud/dashboard/static/pico.min.css 헤더 기준. 별도 internal/cloud/dashboard/static/styles.css와 함께 사용합니다.
Pi 통합gentle-engram 0.5.0plugin/pi/package.json. 개발 의존성 typebox 1.3.34, Pi TUI 피어 범위 >=0.74.0.
Obsidian 통합engram-brain 1.0.0plugin/obsidian/package.json. typescript ^5.0.0, esbuild ^0.21.0, obsidian ^1.4.0.

캐럿(^)이 붙은 값은 허용 범위이며, 정확히 그 버전이 설치된다는 뜻은 아닙니다. Pi의 * 피어 의존성도 고정 버전이 아닙니다. 웹 대시보드에는 React SPA를 빌드하는 루트 package.json이 없고, 플러그인별 Node.js 생태계가 따로 있습니다.

용어
서버 렌더링과 부분 갱신
서버가 HTML을 만들어 보내는 것이 서버 렌더링입니다. HTMX는 필요한 영역만 다시 받아 바꿀 수 있게 합니다. dashboard.Mount()의 페이지 경로와 부분 응답 경로를 비교하면, JSON API와 다른 화면 구성법을 배울 수 있습니다.

3-3. 인프라·빌드·검증

항목설정에서 확인한 값배울 점
로컬 DBWAL, busy_timeout(5000), synchronous(NORMAL), foreign_keys(1)저장소의 동시성과 내구성 정책을 DSN에 명시합니다.
Cloud 개발 구성postgres:16-alpine, 포트 18080docker-compose.cloud.yml에서 DB 준비 상태를 기다립니다.
컨테이너golang:1.25.10-alpine → alpine:3.21docker/cloud/Dockerfile의 다단계 빌드. 실행 사용자는 UID 10001.
배포 바이너리CGO_ENABLED=0, linux/darwin/windows × amd64/arm64.goreleaser.yaml의 6개 OS·아키텍처 조합과 체크섬 생성.
정적 분석golangci-lint 2.13.2Makefile과 CI에서 버전을 맞춥니다.
CIGo 1.25.10, Node.js 24단위 테스트, 서버 E2E, Windows 전용 확인, 플러그인 빌드를 나눕니다.

스택을 읽을 때는 사용자가 실행하는 부품과, 개발·배포할 때만 필요한 도구를 분리해야 합니다. CI의 Node.js 24를 보고 모든 engram 사용자가 Node.js를 설치해야 한다고 해석하면 구조를 놓칩니다.

.github/workflows/ci.yml은 make templ 뒤 생성된 *_templ.go의 차이를 검사합니다. 이어 go test ./...와 실행 파일의 빠른 자체 검증을 구성합니다. 이 설정은 검증 의도를 보여주며, 특정 환경의 실제 통과 결과나 성능 수치를 대신하지는 않습니다.

4아키텍처 심화 분석

입구는 여러 개, 로컬 저장 규칙은 하나 — 네 가지 설계 패턴으로 읽기

4-1. 전체 시스템 구조

[코딩 에이전트] [사람] [연동 프로그램] | MCP stdio | CLI / TUI | HTTP v v v internal/mcp cmd/engram + internal/tui internal/server \ | / +----------------+----------------------+ v internal/store +---------------------------------------+ | SQLite: sessions / observations | | user_prompts / memory_relations | | FTS5 색인 / operation 장부 | | sync_mutations: 등록 프로젝트의 변경 | +---------------------------------------+ | +------------+----------------------+ | 청크 공유·Cloud 청크 전송 | 변경 단위 복제 v v internal/sync internal/cloud/autosync | | +------+---------------+ | | | | v v v FileTransport RemoteTransport MutationTransport manifest.json | HTTP | HTTP chunks/*.jsonl.gz +---------+----------+ v engram cloud serve cloudserver + auth | | v v cloudstore dashboard PostgreSQL templ + HTMX

실행 진입점은 cmd/engram/main.go입니다. engram mcp는 표준 입력·출력으로 통신하는 서버이고, engram serve는 기본 127.0.0.1:7437의 로컬 JSON API입니다. engram cloud serve는 PostgreSQL과 웹 대시보드를 가진 별도 실행 모드입니다.

파일 첫머리의 오래된 사용법 주석에는 serve가 “HTTP + MCP”라고 적혀 있지만, 실제 분기는 cmdServe()와 cmdMCP()로 나뉩니다. 실행 경로를 확인할 때는 요약 주석보다 명령 분기와 핸들러 등록을 우선해야 합니다.

4-2. 패턴 ① 얇은 입구와 공통 저장 규칙

internal/mcp/mcp.go의 NewServerWithConfig()와 registerToolsWithQueue()는 MCP 도구와 핸들러를 묶습니다. handleSave()는 인자를 해석하고 프로젝트·세션을 확인한 뒤 Store.AddObservation()을 호출합니다. CLI의 cmdSave()도 같은 저장 계층으로 이어집니다.

입구마다 JSON·명령행 인자·키보드 메시지의 모양은 다르지만, 제목 검증, 비공개 구간 제거, 토픽 갱신, 중복 판정, 동기화 변경 기록은 internal/store가 맡습니다. 이런 구조를 어댑터와 도메인 분리라는 관점으로 읽을 수 있습니다.

// internal/store/runner.go의 실제 인터페이스
type SemanticRunner interface {
    Compare(ctx context.Context, prompt string) (SemanticVerdict, error)
}

internal/server/server.go에는 SemanticRunnerFactory와 SemanticPromptBuilder가 있습니다. 서버가 LLM 구현 패키지를 직접 소유하지 않고, cmd/engram에서 필요한 함수를 주입합니다. 기능을 공유하되 패키지 순환 참조를 피하는 구체적인 사례입니다.

비유

전화 주문과 앱 주문이 같은 주방으로 들어갑니다. 주문을 받는 형식은 달라도 조리 규칙은 주방에 있어야 합니다. MCP와 HTTP마다 중복 판정을 따로 구현하면 같은 기억을 저장해도 결과가 달라질 수 있습니다.

4-3. 패턴 ② 트랜잭션 장부로 재시도와 동기화를 묶기

AddObservationWithResult()는 저장 전에 프로젝트를 정규화하고, stripPrivateTags()와 prepareStoredContent()로 본문을 정리합니다. 제목·본문을 검증한 다음 withWriteTimings()의 트랜잭션 안에서 다음 분기를 처리합니다.

저장 요청 | +-- OperationID가 이미 있는가? | +-- 같은 fingerprint, 대상이 살아 있음 → 기존 ID, Replayed=true | +-- 같은 fingerprint, 대상이 삭제됨 → 만료 오류 | +-- 다른 fingerprint → 충돌 오류 | +-- 같은 project + scope + topic_key가 있는가? | +-- 이전 버전 보관 → 기존 행 갱신, revision_count 증가 | +-- 최근 중복 조건에 맞는가? | +-- 기존 행의 duplicate_count 증가 | +-- 나머지 → 새 observation 삽입 | v operation 장부 + (복제 등록 시) sync_mutations | v 같은 트랜잭션 COMMIT

recordObservationSaveOperationTx()는 선택적으로 observation_save_operations에 작업 ID·fingerprint·기록 ID를 쓰고, enqueueSyncMutationTx()로 전송 대상 변경을 기록하는 경로를 호출합니다. 다만 enqueueSyncMutationWithSourceTx()는 isProjectEnrolledTx()로 복제 등록 여부를 검사합니다. 미등록 프로젝트는 로컬 저장만 유지하고 Cloud 변경 행을 쌓지 않으며, 등록할 때 현재 상태를 백필합니다. 등록된 프로젝트에서 저장과 변경 예약의 틈을 줄이는 트랜잭션 아웃박스로 해석할 수 있습니다.

복제에 등록된 프로젝트에서는 기억 본문과 전송할 변경이 같은 트랜잭션에서 함께 확정됩니다. 클라우드 전송 자체가 이 트랜잭션 안에서 끝난다는 뜻은 아닙니다. 네트워크 실패는 나중에 재시도하고, 로컬 기록은 먼저 확정합니다.

장치막으려는 문제실제 판정
토픽 갱신같은 주제의 최신 결론이 여러 행으로 흩어짐같은 프로젝트·범위·topic_key의 살아 있는 행을 갱신하고 이전 버전을 보관.
내용 중복 제거짧은 시간에 같은 기록을 반복 저장기본 15분(기록 생성 시각 기준), 정규화 본문 해시 + 프로젝트·범위·종류·제목을 비교.
작업 ID 장부응답 유실 뒤 재시도가 부수 효과를 반복같은 OperationID와 fingerprint면 원래 결과를 반환. 다른 내용이면 충돌.

세 장치는 같은 문제가 아닙니다. 토픽 갱신은 내용을 바꾸고, 중복 제거는 중복 횟수를 올리지만, 동일 작업의 재시도는 원래 결과를 돌려주며 같은 변경을 반복하지 않도록 합니다. 작업 ID를 주지 않는 호출에는 장부 기반 보장이 적용되지 않습니다. 현재 MCP의 handleSave()는 저장 매개변수에 OperationID를 넘기지 않으므로, 저장 계층의 기능을 모든 MCP 저장의 재실행 보장으로 확대하면 안 됩니다.

용어
멱등성 (idempotency)
같은 요청을 다시 보내도 의도한 결과가 중복 적용되지 않게 하는 성질입니다. “서버는 저장했는데 응답을 못 받았다”는 상황에서 중요합니다. internal/store/observation_save_operation_test.go는 같은 작업 ID의 반복과 내용 충돌을 별도로 다룹니다.

4-4. 패턴 ③ 인덱스와 원본을 동기화하는 검색 투영

원본은 observations, 검색용 구조는 observations_fts입니다. FTS 테이블은 content='observations'와 content_rowid='id'로 원본에 연결되며, 삽입·수정·삭제 트리거가 색인을 갱신합니다. migrateFTS()는 스키마와 트리거를 점검합니다.

-- internal/store/store.go의 검색 테이블 선언
CREATE VIRTUAL TABLE IF NOT EXISTS observations_fts USING fts5(
    title, content, tool_name, type, project, topic_key,
    tokenize='trigram',
    content='observations',
    content_rowid='id'
);

SearchContext()와 MCP용 SearchPreviewsContext()는 세 글자 미만 검색어가 있으면 hasShortFTSTerm()을 통해 LIKE 경로로 보냅니다. 나머지는 FTS5로 검색합니다. 세 글자는 바이트 길이가 아니라 utf8.RuneCountInString()으로 셉니다.

질의 예시주요 경로읽어야 할 차이
토큰갱신trigram FTS5세 글자씩 겹치는 조각을 이용합니다. 한국어 형태소 분석기는 아닙니다.
토큰LIKE 대체 경로두 글자도 찾을 수 있지만 FTS 순위 대신 수정 시점 순으로 반환합니다.
토큰 갱신짧은 단어가 있으므로 LIKE기본 all은 단어 조건을 AND로, any는 OR로 결합합니다.
auth/refresh-race정확한 토픽 조회도 시도슬래시가 있으면 topic_key 직접 일치 경로를 추가로 사용합니다.

buildSearchFTSQueryWithColumns()의 BM25 가중치는 제목 5.0, 본문 1.0, 토픽 3.0입니다. 고정(pinned) 여부·최근성·수정 및 중복 횟수로 정렬값도 보정합니다. FTS5의 BM25는 더 작은 음수 값이 좋은 결과이며, 코드도 보정한 값의 오름차순으로 정렬합니다.

정렬에 쓰는 복합 값과 결과의 rank 필드는 구분해야 합니다. SQL은 rank에 원래 BM25 값을 실어 주고, ORDER BY에는 보정한 식을 씁니다. 숫자 하나만 보고 최종 순위를 설명하면 틀릴 수 있습니다.

MCP의 handleSearch()는 기본적으로 미리보기를 조회합니다. SQL의 substr(o.content, 1, 300)으로 본문 일부만 가져오고, 필요할 때 상세 도구를 호출하게 합니다. 이 300은 SQLite 문자열 기준의 문자 수이며, 저장 본문 제한의 바이트 수와 다릅니다.

4-5. 패턴 ④ 전송 인터페이스로 파일 공유와 Cloud 복제 연결

internal/sync/transport.go의 Transport는 저장 매체를 네 메서드로 추상화합니다. 파일 구현은 FileTransport, 원격 청크 구현은 internal/cloud/remote/transport.go의 RemoteTransport입니다. 동기화 알고리즘이 파일 경로와 HTTP 요청을 직접 모두 알 필요를 줄입니다. 백그라운드 복제는 internal/cloud/autosync의 별도 CloudTransport 계약으로 변경을 주고받으며, cmd/engram/main.go에서 MutationTransport를 어댑터로 연결합니다.

// internal/sync/transport.go의 실제 계약
type Transport interface {
    ReadManifest() (*Manifest, error)
    WriteManifest(m *Manifest) error
    WriteChunk(chunkID string, data []byte, entry ChunkEntry) error
    ReadChunk(chunkID string) ([]byte, error)
}

FileTransport.WriteChunk()는 청크를 gzip으로 압축해 저장합니다. chunkcodec.ChunkID()는 SHA-256 결과의 앞 8개 16진수 문자를 ID로 사용합니다. 콘텐츠에서 ID를 만들지만 전체 SHA-256을 ID로 보관하는 설계는 아니며, 이 짧은 ID를 보안 인증값으로 해석하면 안 됩니다.

주석과 확장자는 .jsonl.gz라고 표현합니다. 그러나 internal/sync/sync.go의 내보내기는 jsonMarshalChunk(chunk)를 호출하고, 기본 함수는 json.Marshal입니다. 실제 청크는 세션·기록·변경을 묶은 JSON 객체이므로, “한 줄마다 메모리 하나”라고 단정해서 파서를 만들면 안 됩니다.

4-6. 동시성과 복구: 위 패턴이 실제로 버티게 하는 장치

storeDSN()은 _txlock=immediate를 지정합니다. 읽은 뒤 쓰기로 전환하다가 다른 쓰기와 충돌하는 일을 줄이기 위해 트랜잭션 시작에 쓰기 예약을 잡습니다. WAL과 5초 잠금 대기, 저장 계층의 잠금 오류 재시도도 함께 사용합니다.

db.SetMaxOpenConns(1)은 각 Store의 연결 풀을 하나로 제한합니다. MCP 프로세스가 여러 개면 데이터베이스 전체 연결이 하나인 것은 아닙니다. internal/mcp/write_queue.go는 기본 크기 32의 채널로 쓰기를 직렬화하고, 가득 차면 재시도를 안내합니다.

최근 커밋의 diagnostic.BuildRepairPlan()은 vetoProjectAliasMoves()로 현재 별칭과 저장된 디렉토리 증거를 다시 읽습니다. 별칭이 이동 대상과 충돌하면 project_alias_contradicts_reclassification 사유로 제외합니다. 별칭은 잘못된 이동을 막는 증거이며, 그것만으로 과거 기록의 소유자를 바꾸도록 허가하지는 않습니다.

설계를 읽는 질문
정상 경로 다음에는 실패 순서를 바꿔 보기

“저장 직후 응답이 끊기면?”, “검색 원본만 수정되면?”, “진단 뒤 별칭이 바뀌면?”을 물어보세요. 장부, 트리거, 증거 재확인은 각각 이 질문에 대한 코드 수준의 답입니다.

5디렉토리 구조 해부

폴더 이름보다, 어느 계층이 무엇을 결정하는지 읽기
engram/ ├── cmd/engram/ 명령 분기·런타임 조립·Cloud 명령 ├── internal/ │ ├── store/ SQLite·FTS·중복·소유권·변경 기록 │ ├── mcp/ 에이전트 도구·응답 투영·쓰기 큐 │ ├── server/ 로컬 HTTP API·런타임 세션 계약 │ ├── tui/ 터미널 상태·입력 처리·화면 렌더링 │ ├── project/ 프로젝트 탐지·식별·해석 규칙 │ ├── diagnostic/ 진단·복구 계획·안전 조건 │ ├── sync/ 청크 내보내기·가져오기·전송 계약 │ ├── cloud/ │ │ ├── autosync/ 백그라운드 복제 관리 │ │ ├── remote/ HTTP 전송 구현 │ │ ├── chunkcodec/ 청크 ID·정규화·검증 │ │ ├── cloudserver/ Cloud HTTP·권한 경계 │ │ ├── cloudstore/ PostgreSQL·프로젝트 통제·감사 기록 │ │ ├── dashboard/ templ 화면·정적 CSS·HTMX │ │ └── auth/ 인증·토큰 관련 공통 규칙 │ ├── llm/ 선택적 외부 CLI 의미 비교 │ ├── setup/ 에이전트별 통합 설정 │ ├── obsidian/ 메모리의 노트·그래프 내보내기 │ └── selftest/ 실행 파일 자체 검증 기능 ├── plugin/ claude-code·codex·opencode·pi·obsidian ├── docs/ 아키텍처·계약·설치·운영·복구 문서 ├── tools/ 계약 생성 도구·동기화 보조 도구 ├── scripts/ 성능·죽은 코드 증가 방지 검사 ├── docker/cloud/ Cloud 이미지 빌드 ├── .github/workflows/ CI·배포·이슈 정책 자동화 ├── go.mod / go.sum Go 모듈 선언·검증 정보 ├── Makefile 템플릿 생성·린트·성능 검사 진입점 └── .goreleaser.yaml 다중 플랫폼 릴리스 설정

5-1. 처음 읽는 사람의 경로

순서읽을 파일·함수확인할 질문
① 입구cmd/engram/main.go
cmdSave(), cmdSearch()
인자는 어떤 구조체로 바뀌고, 언제 저장소가 열리는가?
② 저장internal/store/store.go
AddObservationWithResult()
새 행·토픽 갱신·중복·재실행의 분기는 어디에 있는가?
③ 검색같은 파일의 SearchContext()프로젝트 필터와 검색어 길이가 SQL을 어떻게 바꾸는가?
④ 에이전트internal/mcp/mcp.go
handleSearch()
SQL 결과가 에이전트가 읽을 응답으로 어떻게 바뀌는가?
⑤ 확장internal/sync/transport.go같은 알고리즘에서 저장 매체를 어떻게 교체하는가?

파일을 처음부터 끝까지 읽기보다, 사용자 행동 하나를 골라 입구에서 저장소까지 따라가면 구조가 보입니다. 특히 internal/store/store.go는 약 14,444줄입니다. 첫 정독 대상으로 삼기보다 함수명과 호출 경로를 기준으로 나눠 읽는 편이 낫습니다.

5-2. 언어별 코드 규모

커밋 ab461b9의 Git 추적 파일을 확장자별로 구분한 물리적 줄 수입니다. 빈 줄과 주석을 포함하며, 문서·이미지·JSON은 아래 합계에 넣지 않았습니다. 생성 코드는 따로 표시했습니다. 압축된 JS·CSS는 한 줄에 많은 코드가 들어갈 수 있습니다.

구분파일 수줄 수
Go 전체596254,967
Go 구현18271,475 — 테스트·templ 생성 코드 제외
Go 테스트411178,241 — *_test.go
Go 생성 코드35,251 — *_templ.go
TypeScript66,535
JavaScript10122,441 — .js 453줄 + .mjs 21,988줄
templ31,584
CSS21,640
셸·PowerShell234,127 — .sh 3,627줄 + .ps1 500줄

테스트가 많다는 사실은 읽을 수 있는 동작 사례가 많다는 뜻입니다. 테스트 줄 수만으로 결함 수, 커버리지, 운영 안정성을 추정할 수는 없습니다. 코드 줄 수는 개발 이력의 길이나 장기간 기여 비율을 알려주지 않습니다.

6학습 포인트 (기술별)

기술 이름을 외우는 대신, 각 기술이 지키는 약속을 찾아보기

6-1. Go: 인터페이스는 필요한 쪽의 계약이다

Transport는 파일 시스템과 HTTP라는 구현보다 “청크를 읽고 쓴다”는 요구를 먼저 드러냅니다. SemanticRunner는 외부 CLI 이름 대신 비교 결과의 형태를 계약으로 고정합니다. Go에서는 구현체에 별도의 implements 선언이 없어도 메서드가 맞으면 인터페이스를 만족합니다.

소스의 if err != nil은 장식이 아닙니다. “파일이 없다”와 “읽을 권한이 없다”를 모두 빈 데이터로 취급하면 복구 동작이 위험해집니다. FileTransport.ReadManifest()가 이 둘을 구분하는 방식을 확인해 보세요.

6-2. SQLite: 같은 트랜잭션에 넣어야 할 것을 고르기

withWriteTimings()는 시작·본문 실행·커밋을 공통으로 감싸고 실패하면 롤백합니다. 핵심은 문법보다 묶음의 범위입니다. 기록과 선택적 작업 장부, 복제 등록된 프로젝트의 동기화 변경을 같은 묶음으로 다룹니다.

용어
불변 조건 (invariant)
실행 순서나 실패가 달라져도 깨지면 안 되는 약속입니다. 예를 들어 “복제 등록된 프로젝트의 저장 변경에는 전송할 변경 기록이 있다”는 약속을 세울 수 있습니다. 테스트는 기능 이름보다 이런 약속을 기준으로 읽으면 이해가 빠릅니다.

observations.deleted_at은 기록을 숨기는 소프트 삭제에 쓰입니다. 검색 SQL에 o.deleted_at IS NULL이 들어가는 이유를 원본 보존과 사용자 노출의 분리로 읽어 보세요.

6-3. 검색: 좋은 제목과 언어별 경계 조건

기본 검색은 임베딩 유사도가 아니라 FTS5의 문자열 기반 검색입니다. 제목과 토픽의 가중치가 높으므로 “버그 수정”보다 “토큰 갱신 경합으로 중복 요청 발생”처럼 원인을 담은 제목이 유용합니다. 검색 품질은 랭킹 식뿐 아니라 기록 품질에도 달려 있습니다.

escapeLIKE()는 %와 _를 이스케이프합니다. SQL 매개변수 바인딩은 SQL 구조 삽입을 막는 문제이고, 와일드카드 이스케이프는 검색 의미를 보존하는 문제입니다. 둘 중 하나만 해서는 같은 효과를 얻지 못합니다.

6-4. MCP·HTTP: 같은 데이터, 다른 계약

MCP는 모델이 호출할 도구의 이름·인자·결과를 제공합니다. ResolveTools()는 프로필 이름과 도구 이름의 쉼표 목록을 해석합니다. 모든 도구가 항상 필요하지 않은 환경에서는 도구 집합도 구성 요소가 됩니다.

# 필요한 도구만 노출하는 stdio 서버 구성 예시
engram mcp --project study-app --tools=mem_search,mem_get_observation

HTTP에서는 경로·메서드·상태 코드·인증이 별도의 계약이 됩니다. internal/server/server.go의 routes()와 internal/cloud/cloudserver의 경계를 섞지 말고 읽어야 합니다. 같은 실행 파일에 들어 있어도 로컬 API와 Cloud API가 같은 권한 모델인 것은 아닙니다.

도구를 연결하는 문제와 도구가 다룰 데이터의 소유권을 확인하는 문제는 각각 설계해야 합니다. 저장 계층의 UpdateObservationForProject()·DeleteObservationForProject()는 호출자가 주장한 프로젝트와 실제 소유 프로젝트를 트랜잭션 안에서 비교합니다. 이 일관성 확인만으로 사용자를 인증하는 것은 아닙니다.

6-5. 화면: TUI의 메시지 루프와 웹의 HTML 조각

internal/tui/model.go의 Init()는 초기 명령을 반환하고, internal/tui/update.go의 Update()는 입력이나 검색 결과 메시지로 상태를 바꿉니다. internal/tui/view.go의 View()는 현재 상태를 문자열 화면으로 표현합니다.

키 입력 → Update(상태 변경) → 검색 명령 반환 | v 저장소 검색 실행 | v 검색 결과 메시지 → Update(결과 저장) → View(새 화면)

웹 쪽에서는 dashboard.Mount()가 전체 페이지와 부분 응답의 경로를 등록합니다. internal/cloud/dashboard/embed.go의 go:embed static은 CSS·HTMX를 실행 파일에 포함합니다. TUI와 웹 모두 “현재 상태를 어디서 관리하고, 어떤 입력이 상태를 바꾸는가”라는 질문으로 읽을 수 있습니다.

6-6. 테스트·배포: 정상 결과보다 경계 조건을 읽기

테스트 파일읽을 동작학습 질문
internal/store/store_fts_test.go짧은 질의, CJK 검색, 색인 수명주기, 기존 FTS 업그레이드언어·데이터 변경·과거 스키마가 검색을 어떻게 깨뜨리는가?
internal/store/observation_save_operation_test.go재실행·충돌·삭제 후 만료·재시작·롤백첫 성공 이후 같은 요청이 와도 부수 효과가 반복되지 않는가?
internal/store/concurrency_repro_test.goDEFERRED와 IMMEDIATE, 프로세스 간 쓰기 경합같은 프로세스의 큐만으로 여러 프로세스의 잠금까지 해결되는가?
internal/mcp/write_queue_test.go큐 포화·취소·panic 이후 작업자 유지실패가 뒤의 요청을 영구적으로 막지 않는가?
internal/diagnostic/project_alias_test.go진단 이후 추가된 별칭도 복구 계획에서 재확인이전 보고서의 증거를 현재 사실로 믿어도 되는가?

저장·검색·동시성 테스트는 internal/store/에, MCP·진단 테스트는 internal/의 각 패키지에 있습니다. CI의 검증 구성과 배포 대상 6개 조합을 함께 보면, “내 컴퓨터에서 동작”을 여러 운영체제의 제품으로 넓히는 비용이 보입니다.

7하드웨어 / 시스템 요구사항

기본 실행, 소스 빌드, 선택 기능의 요구를 구분하기
사용 방식필요한 환경구분할 점
로컬 바이너리macOS·Linux·Windows, amd64 또는 arm64용 실행 파일, 쓰기 가능한 로컬 디스크Go·Node.js·Python·Docker는 기본 로컬 메모리 실행의 필수 조건이 아닙니다.
소스 빌드go.mod의 Go 1.25.10 요구를 만족하는 도구 체인설치 문서의 “Go 1.24+”보다 현재 모듈 선언을 우선해서 확인합니다.
MCP 연동stdio MCP를 지원하는 에이전트와 실행 파일 경로 설정일반 stdio 구성은 별도 HTTP 서버 실행 없이 사용할 수 있습니다.
HTTP API사용 가능한 로컬 포트, 기본 7437ENGRAM_PORT로 조정. Cloud 기본 포트는 8080이며, 개발 Compose에서는 18080로 바꿉니다.
Cloud 서버PostgreSQL 연결, 인증 설정, 서버 운영 환경개발 Compose는 PostgreSQL 16과 Cloud 포트 18080을 사용합니다.
플러그인 개발해당 에이전트·앱, Node.js 계열 도구플러그인 빌드 요구를 Go 코어 전체의 실행 요구로 확대하지 않습니다.
선택적 의미 비교claude 또는 opencode CLI와 사용 환경llm.NewRunner() 기준. 모델 사용 비용·네트워크는 해당 CLI 설정에 따릅니다.

공식 문서에는 CPU 코어 수, 최소 RAM, 처리량에 대한 정량 보장이 없습니다. 기본 저장·FTS 검색에 GPU를 요구하는 모델 실행 경로는 없습니다. 에이전트 모델 자체의 실행 환경은 engram 로컬 저장소의 요구사항과 별도로 판단해야 합니다.

실행 파일은 가벼운 배포 형태이지만, 데이터 용량과 동시 쓰기 비용까지 사라지는 것은 아닙니다. DefaultConfig()의 본문 잘림 기준은 50000바이트입니다. 이를 넘으면 UTF-8 문자 경계에서 자른 뒤 “... [truncated]”를 덧붙이므로 저장 문자열은 기준보다 길어질 수 있습니다. 한국어 5만 글자를 뜻하지 않으며, 검색 색인·버전 이력·WAL·백업은 추가 디스크를 사용합니다.

7-1. 용량은 이렇게 나누어 생각한다

기록 10,000개에 본문이 평균 2,000바이트라면 본문 합계만 약 20MB입니다. 모두 50,000바이트를 채우면 본문만 약 500MB입니다. 이는 곱셈으로 구한 본문 크기 예시이며, 실제 DB 파일 크기나 메모리 사용량의 측정값이 아닙니다.

7-2. SQLite 파일 위치도 시스템 설계다

ENGRAM_DATA_DIR의 비어 있지 않은 값은 절대 경로여야 합니다. 저장소의 파일 시스템 검사와 설치 문서는 알려진 NFS·SMB/CIFS 위치를 거부하도록 합니다. 여러 기기의 공유는 DB 파일을 네트워크 드라이브에 놓는 방식보다 청크·Cloud 복제 경로를 이해하고 사용해야 합니다.

운영 경계
개발 Compose와 실행 중 DB 복사는 별도 주의가 필요합니다

docker-compose.cloud.yml은 개발용 비밀번호와 ENGRAM_CLOUD_INSECURE_NO_AUTH=1을 포함합니다. 이 설정을 공개 서버의 인증 구성으로 옮기면 안 됩니다.

WAL 모드에서는 engram.db-wal에도 아직 반영되지 않은 변경이 있을 수 있습니다. 파일 이동은 관련 프로세스를 모두 멈추고 DB·WAL·SHM의 일관성을 유지하는 절차로 다뤄야 합니다.

8직접 해볼 수 있는 실습 과제

난이도별 다섯 과제 — 예상과 실제 결과를 비교하는 방식으로 공부하기

실행 과제는 설치된 engram과 실제 기록이 없는 전용 실습 데이터 디렉토리를 전제로 합니다. 변경하는 과제는 별도 연습용 사본에서 진행하고, 우선 소스와 테스트만 읽어 예상 결과를 적어도 좋습니다.

과제 1입문 · 30~45분

토픽 갱신과 내용 중복을 구분하기

같은 토픽에 다른 내용을 저장하는 경우와, 토픽 없이 같은 제목·본문을 반복하는 경우를 비교합니다. 기록 수만 보지 말고 revision_count와 duplicate_count를 구분해 예상합니다.

# POSIX 셸 예시: 실습 디렉토리 안에서 시작
mkdir -p engram-lab
cd engram-lab
export ENGRAM_DATA_DIR="$PWD/data"

engram save '인증 정책' '만료된 토큰은 한 번 갱신한다.' \
  --project study-app --topic auth/policy
engram save '인증 정책' '동시 갱신은 하나로 합친다.' \
  --project study-app --topic auth/policy
engram search '인증 정책' --project study-app

engram save '검증 기록' '테스트용 동일 본문' --project study-app
engram save '검증 기록' '테스트용 동일 본문' --project study-app

완료 기준: 첫 두 저장은 같은 토픽의 기록 갱신이며 이전 버전이 보관된다는 점, 마지막 두 저장은 기본 중복 판정 시간 안에서 중복 횟수 증가로 이어진다는 점을 설명합니다.

읽을 곳: AddObservationWithResult()의 토픽 분기가 내용 중복 분기보다 먼저 실행되는 순서.

과제 2입문 · 45~60분

한국어 두 글자 검색의 경로 찾기

본문에 “토큰갱신 실패”, “접속 토큰 만료”, “인증 정책 변경”을 넣은 기록을 준비합니다. 토큰·토큰갱신·토큰 정책을 검색하고 AND/OR 차이를 비교합니다.

engram search '토큰' --project study-app
engram search '토큰갱신' --project study-app
engram search '토큰 정책' --project study-app --match all
engram search '토큰 정책' --project study-app --match any

완료 기준: 각 질의의 FTS/LIKE 분기를 맞히고, 두 경로의 정렬 방식이 다른 이유를 설명합니다. 검색 지원을 곧 한국어 형태소 이해로 해석하지 않아야 합니다.

읽을 곳: hasShortFTSTerm(), buildSearchLIKEQueryWithColumns(), TestShortTermFallbackEscapesAndPreservesFiltersAndMatchModes.

과제 3중급 · 2~3시간

응답 유실을 가정한 작업 장부 실험 설계

TestAddObservationWithOperationIDIsIdempotent를 바탕으로 별도 실습 사본에 표 형태의 사례를 추가합니다. 같은 ID·같은 내용, 같은 ID·다른 내용, 다른 ID·같은 내용, 기존 기록 삭제 뒤 재시도를 구분합니다.

완료 기준: 기록 수뿐 아니라 작업 장부와 변경 기록의 수가 왜 그렇게 되는지 설명합니다. 복제 미등록 프로젝트에서는 Cloud 변경 기록이 0행일 수 있다는 조건도 반영합니다. 삭제된 대상의 작업 ID가 새 기록 생성 명령으로 바뀌지 않는 이유도 적습니다.

핵심 제한: 저장 계층의 OperationID 지원이 모든 MCP 호출에 자동 적용되는 것은 아닙니다. handleSave()가 만드는 매개변수에 실제로 어떤 필드를 넘기는지 확인합니다.

과제 4중급 · 반나절

네트워크 없이 전송 실패를 재현하는 모형 만들기

Transport의 네 메서드를 메모리 안의 맵으로 구현하는 연습 코드를 만듭니다. 두 번째 청크 쓰기에서 의도적으로 오류를 반환하고, 다시 시도했을 때 어떤 청크와 변경이 남아야 할지 먼저 표로 정의합니다.

완료 기준: 재시도 후 누락 없이 복구되는 조건과, 모형이 검증하지 못하는 실제 파일·HTTP 실패를 구분합니다. 외부 Cloud 서버 없이도 핵심 상태 전이를 공부할 수 있어야 합니다.

과제 5고급 · 하루

오래된 진단 보고서로 잘못된 복구를 막기

internal/diagnostic/project_alias_test.go의 TestProjectAliasGuard를 읽고, 진단 직후 별칭이 추가되는 시간 순서를 그림으로 옮깁니다. 먼저 “별칭 없음 / 대상과 일치 / 다른 대상 / 충돌”에 대한 기대 결과 표를 만듭니다.

완료 기준: “금지할 근거”와 “허용할 근거”가 다르다는 점을 테스트 사례로 설명합니다. 복구 도구는 정상 기능보다 더 강한 증거가 필요하다는 것을 배웁니다.

9관련 기술 심화 학습 로드맵

6주 동안 작은 저장소에서 실패에 강한 시스템으로 넓히기

주마다 “읽기 → 작은 모형 → 실패 사례 → 설명”을 한 번씩 반복합니다. 처음부터 모든 플러그인이나 Cloud 인증을 구현할 필요는 없습니다. 한 경로의 입력·저장·응답을 끝까지 설명하는 것이 우선입니다.

주차주제·읽을 코드작은 산출물과 완료 기준
1주Go 기초·CLI 인자
cmdSave(), cmdSearch()
제목·본문·프로젝트를 받는 작은 메모 프로그램을 설계합니다. 구조체와 오류 반환을 설명하고 인자 누락 사례를 정리합니다.
2주SQL·트랜잭션·소유권
AddObservationWithResult()
세션과 기록의 관계도를 만듭니다. 토픽 갱신·중복·소프트 삭제의 전후 상태를 각각 그립니다.
3주FTS5·trigram·BM25
SearchContext()
한국어 질의 10개의 예상 검색 경로를 만듭니다. 짧은 단어 대체 경로와 제목 가중치의 효과를 설명합니다.
4주MCP·HTTP·응답 크기
handleSearch(), routes()
같은 검색을 CLI·MCP·HTTP 입력으로 표현합니다. 미리보기에서 상세 조회로 넘어가는 조건과 프로젝트 선택 규칙을 적습니다.
5주멱등성·아웃박스·복제
Transport, 작업 장부 테스트
응답 유실·부분 전송·중복 재시도의 실패 표를 만듭니다. 로컬 커밋과 원격 확인이 서로 다른 시점임을 보여줍니다.
6주동시성·진단·배포
writeQueue, BuildRepairPlan(), CI
큐 포화와 오래된 복구 증거의 처리 방식을 설명합니다. 운영체제별 검증 항목과 남은 한계를 한 페이지로 정리합니다.

진도를 나가는 기준은 읽은 파일 수보다, 실패했을 때 남는 상태를 스스로 예측할 수 있는지입니다. 예측이 어긋난 지점이 다음에 읽어야 할 함수입니다. 결과만 맞히기보다 “어느 트랜잭션과 검증이 그 결과를 보장하는가”까지 연결해 보세요.

9-1. 관심사에 따라 이어갈 갈래

자기 점검
최종적으로 답할 수 있어야 할 세 질문

① 같은 저장 요청이 두 번 오면 어떤 분기가 실행되는가? ② 두 글자 검색은 어떤 SQL과 정렬을 사용하는가? ③ 저장은 성공하고 전송은 실패했을 때, 다음 시도에 필요한 증거는 어디에 남는가?

10핵심 키워드 사전

이름을 외우기보다 이 저장소의 실제 역할과 연결하기
용어쉬운 설명
영속 메모리대화나 프로세스가 끝나도 디스크에 남는 기록. 모델의 가중치 학습과는 다른 방식입니다.
로컬 우선내 기기의 저장과 조회를 기본으로 설계하고, 원격 복제를 선택적으로 연결하는 방향입니다.
세션한 번의 작업 흐름을 묶는 단위. 여러 관찰 기록이 같은 세션에 속할 수 있습니다.
Observation제목·본문·종류·프로젝트를 갖춘 기억 한 건. 수정·중복·검토 정보도 함께 관리합니다.
topic_key계속 발전하는 주제의 안정적인 열쇠. 같은 프로젝트·범위에서 기존 기록을 갱신하는 데 씁니다.
scope기억을 어떤 범위에서 사용할지 나타내는 값. project·personal·global이 있습니다.
MCPAI 애플리케이션과 도구 서버가 기능·인자·결과를 주고받는 공통 프로토콜입니다.
stdio 전송프로세스의 표준 입력·출력으로 메시지를 교환하는 방식. MCP 서버가 꼭 웹 서버인 것은 아닙니다.
FTS5SQLite 안에서 긴 텍스트를 찾고 순위를 매기는 전문 검색 기능입니다.
역색인문자열 조각이 어느 기록에 있는지 미리 정리한 찾아보기. 매번 본문 전체를 읽는 비용을 줄입니다.
trigram겹치는 세 글자 조각으로 검색 대상을 구성하는 방식. 형태소나 의미를 이해하는 모델은 아닙니다.
BM25단어의 희소성·출현 횟수·문서 길이 등을 이용한 관련도 점수. FTS5에서는 작은 값이 더 좋은 일치입니다.
검색 투영원본에서 검색·표시에 필요한 형태를 따로 만드는 것. FTS 색인과 짧은 미리보기가 서로 다른 예입니다.
트랜잭션여러 데이터 변경을 한 묶음으로 성공시키거나 취소하는 장치. 본문과 장부의 어긋남을 막는 데 씁니다.
WAL변경을 먼저 로그에 남기는 SQLite 저널 방식. DB 본체 외의 파일도 일관성에 영향을 줍니다.
BEGIN IMMEDIATE트랜잭션 시작에 쓰기 예약을 확보하는 방식. 읽은 뒤 쓰기로 바꿀 때의 경합을 줄이려는 선택입니다.
멱등성같은 요청을 반복해도 의도한 변경이 중복 적용되지 않게 하는 성질입니다.
Fingerprint요청 내용을 구분하기 위한 지문. 같은 작업 ID로 다른 내용을 보내는 충돌을 판별합니다.
트랜잭션 아웃박스전송 대상 데이터의 변경과 나중에 전송할 변경 기록을 같은 트랜잭션에 쓰는 패턴입니다.
청크·manifest공유할 기록을 묶은 덩어리와 그 목록. manifest의 이름만으로 실제 청크 존재를 보장할 수는 없습니다.
소프트 삭제즉시 물리 삭제하지 않고 삭제 시점을 표시해 일반 조회에서 숨기는 방식입니다.
인증·소유권 확인누구인지 확인하는 일과, 요청한 프로젝트가 데이터의 실제 소유자와 맞는지 확인하는 일입니다.
백프레셔처리 용량이 찼을 때 더 받지 않고 호출자에게 기다리거나 재시도하도록 알리는 제어입니다.
templ·HTMX서버에서 HTML을 생성하는 도구와 브라우저에서 HTML 영역을 부분 갱신하는 도구입니다.
CGOGo에서 C 코드와 연결하는 기능. 배포 설정의 CGO_ENABLED=0은 이를 사용하지 않는 빌드를 선택합니다.

scope=personal이나 프로젝트 소유권 검사가 디스크 암호화를 의미하지는 않습니다. 기능의 이름이 무엇을 약속하는지, 실제 저장·조회 조건이 무엇인지 나누어 이해해야 합니다.

11참고 링크

공식 문서로 전체 맥락을, 고정된 소스 링크로 구현 근거를 확인하기

11-1. 공식 저장소와 사용 문서

11-2. 본문의 핵심 코드 근거

11-3. 관련 기술 공식 학습 자료

현재 상태 (2026-10-23 확인)

항목상태
저장소Gentleman-Programming/engram
선정 기준GitHub Trending Go Weekly #7
스타 / 포크7,554 / 789 — 2026-10-23 GitHub 기준
주 언어Go
생성일2026-02-16
최신 릴리스v3.5.2 — 2026-10-21
최근 push2026-10-23
최근 커밋ab461b9 — 2026-10-23
fix(doctor): veto project repairs that contradict aliases (#2056)
Go 코드 규모596파일, 약 25.5만 줄. 구현 71,475줄 / 테스트 178,241줄 / 생성 코드 5,251줄.
그 밖의 코드TypeScript 약 6.5천 줄, JS·MJS 약 2.2만 줄, templ 1,584줄, CSS 1,640줄.
실행·저장 형태Go 바이너리 + 로컬 SQLite·FTS5. Cloud는 선택형 PostgreSQL 복제·공유 계층.
라이선스MIT — 저장소의 LICENSE 기준
쓰기 전에 알아둘 한계
기억을 보존하는 시스템도 내용의 정확성까지 보장하지는 않습니다

기록 품질: 저장된 결론은 틀리거나 오래될 수 있습니다. What / Why / Where / Learned 형식은 에이전트 지침이며, 저장 계층이 모든 본문에서 이를 강제하지는 않습니다. 검색한 기억은 현재 코드와 대조해야 합니다.

검색·판단: 기본 검색은 어휘 중심입니다. 의미 비교는 internal/llm의 외부 CLI 선택 기능이며, 기본 검색을 자동으로 벡터 검색으로 바꾸지 않습니다. 관련 후보가 있다는 것과 실제 모순이라는 판단도 구분해야 합니다.

비공개 정보: <private>...</private> 제거는 명시된 구간을 가리는 장치입니다. 비밀 자동 탐지, 디스크 암호화, 완전한 유출 방지를 뜻하지 않습니다. 프롬프트·메모리·공유 청크에 넣을 내용을 직접 검토해야 합니다.

버전·증거 범위: 소스 설명은 커밋 ab461b9 기준이며 최신 릴리스 v3.5.2와 완전히 같은 시점이 아닙니다. 테스트 코드의 존재만으로 전체 테스트 통과나 운영 성능을 보증할 수는 없습니다. 코드 규모는 메모리 사용량·처리량의 측정값이 아닙니다. 경쟁 제품 비교와 오래된 요구 버전 문구도 현재 구현과 별도로 확인해야 합니다.