본문으로 건너뛰기

서버 및 SSOT 아키텍처

상태: main의 제한된 단일 노드 typed 원격 SSOT profile은 Phase 1을 완료했습니다. 여전히 workspace 전용 engineering 계약이며 production/hosted 배포 계약은 아닙니다. PostgreSQL, 고가용성, managed deployment는 Phase 2 이후 작업으로 남습니다.

현재 AideMemo는 로컬 우선 시스템입니다. Rust 코어가 하나의 임베디드 저장소를 열고 stdio MCP, 로컬 daemon 또는 aidememo mcp-serve를 통해 이를 조정합니다. 이 방식은 한 사용자 또는 신뢰된 에이전트 집단에 계속 가장 단순한 모드입니다.

서버 목표에서는 소유권 경계가 바뀝니다. 서버가 정본이 되고 로컬 저장소는 지연이 낮은 읽기 cache와 명시적인 offline branch가 됩니다. 이 문서는 단일 노드 서버, Cloudflare 기반 SaaS, 온프레미스 Kubernetes 배포에서 동일하게 유지할 invariant를 고정합니다. 아직 이 배포 모드가 출시됐다는 주장이 아니라 아키텍처 결정과 구현 순서입니다.

결정

구조화된 도메인 레코드와 프로젝트별 순서가 있는 change log를 중심으로 이식 가능한 메모리 서비스를 만듭니다. 검색 index와 파일형 artifact는 파생 또는 하위 데이터 plane으로 둡니다.

세 배포 profile은 하나의 protocol과 conformance suite를 공유합니다.

Profile정본 레코드Artifact 본문조정
임베디드 로컬SQLite로컬 파일시스템프로세스 + SQLite transaction
호스팅 SaaS초기 PostgreSQL, 이후 선택형 project Durable Object adapterS3 호환 저장소, 첫 preset은 R2Database CAS, 활성 협업에는 선택형 project Durable Object
온프레미스 KubernetesPostgreSQL고객의 S3 호환 저장소Database transaction과 CAS

Cloudflare는 효율적인 호스팅 profile이지 제품 의미론의 정의가 아닙니다. R2는 S3 호환 계약을 통해 접근합니다. Durable Object는 프로젝트 크기의 coordination 경계에 사용하며 하나의 전역 AideMemo singleton이 되어서는 안 됩니다. 완전한 Durable Object 레코드 저장 adapter는 PostgreSQL 및 로컬 adapter와 동일한 conformance와 논리 export 검사를 통과한 뒤에만 허용합니다.

현재 경계와 서버 경계

현재 공유 저장소 모델은 의도적으로 협력하는 에이전트용 partition입니다.

  • source_id는 fact와 source에서 볼 수 있는 graph 데이터를 필터링합니다.
  • actor_id는 작성자 provenance를 기록합니다.
  • bearer-token binding은 네트워크 호출자가 두 값을 재정의하지 못하게 할 수 있습니다.
  • entity name과 type은 저장소 내부의 하나의 공유 ontology를 유지합니다.

이는 상호 불신 tenant 격리가 아닙니다. 특히 entity는 공유 레코드이고 fact attachment를 통해 source에서 볼 수 있는 entity 접근을 계산합니다. 서버 모델은 source_id를 tenant credential로 승격하지 않고 독립된 identity를 도입합니다.

tenant_id
`- project_id
|- source_id
|- actor_id
`- resources: entities, facts, relations, sessions, handoffs, artifacts

tenant_id, project_id, actor_id는 인증된 서버 context에서 파생합니다. 클라이언트는 이를 생략할 수 있지만 command body에서 범위를 넓히거나 교체할 수 없습니다. source_id는 프로젝트 내부의 애플리케이션 namespace이며 billing, authorization 또는 물리적 격리 key가 아닙니다.

모든 정본 unique constraint와 lookup은 tenant와 project identity로 시작합니다.

UNIQUE (tenant_id, project_id, normalized_entity_name)
UNIQUE (tenant_id, project_id, source_id, content_hash)
UNIQUE (tenant_id, project_id, command_id)

시스템 모델

정본 transaction은 도메인 mutation, change entry, audit provenance, outbox 작업을 함께 commit합니다. Object upload와 외부 indexing은 reservation/commit 및 idempotent worker를 통해 transaction 밖에서 수행합니다.

Command 계약

MCP tool, REST 호출, SDK method, offline branch publish를 포함한 모든 변경 surface는 하나의 command envelope로 매핑합니다.

{
"command_id": "01K...",
"project_id": "project_01K...",
"expected_revision": 7,
"operation": "fact.add",
"payload": {}
}

인증된 gateway가 tenant와 actor identity를 제공합니다. 서비스는 다음을 보장해야 합니다.

  1. 인증 membership 밖의 project를 거부합니다.
  2. 이미 commit된 command_id는 저장된 receipt를 반환합니다.
  3. 오래된 expected_revision은 부분 쓰기 없이 거부합니다.
  4. 도메인 row와 change/audit/outbox row를 원자적으로 갱신합니다.
  5. commit된 project sequence와 resource revision을 반환합니다.
  6. handoff worker process가 종료됐다는 이유만으로 task 성공을 추론하지 않습니다.

현재 구현된 /v1/commands 구간은 의도적으로 저수준 조합인 resource.put + upsertresource.delete + delete만 받습니다. Delete payload는 JSON null이어야 하고 resource kind는 custom.* 확장 namespace를 사용해야 합니다. fact, session, handoff, artifact 같은 예약 제품 kind는 원시 endpoint에서 거부합니다. 제품 작업은 이 endpoint의 alias로 받지 않습니다. 별도의 typed route는 이제 session 생성, session에 연결된 fact 생성, handoff send/indexed inbox/outbox/accept/return/status를 지원합니다. Search, heartbeat, MCP 연결은 아직 열려 있습니다. 이 경계는 원시 route가 제품 의미론을 우회하지 못하게 합니다.

Idempotency fingerprint는 project, revision precondition, operation, payload, 전체 resource 좌표, upsert/delete change kind를 결합합니다. 따라서 하나의 command_id를 다른 resource에 재사용하면 첫 resource receipt를 replay하지 않고 command_conflict로 실패합니다.

Handoff claim과 return invariant는 계속 도메인 작업입니다. Handoff 결과 fact는 같은 tenant, project, session, source, 수신 actor, 활성 claim과 일치해야 합니다. Artifact path에 파일을 쓰는 것만으로는 handoff가 완료되지 않습니다. 첫 typed HTTP 구간은 이 검사를 강제하고, 수신자가 활성 writer membership을 가져야 하며, 실패한 return은 새 exclusive claim으로만 재시도할 수 있게 합니다.

순서가 있는 change feed

서버 sync는 record 종류별 ULID watermark 대신 프로젝트별 monotonic sequence 하나를 사용합니다.

{
"project_epoch": "01K...",
"after_seq": 18420,
"limit": 1000
}

각 materialized entry는 seq, resource kind와 ID, operation, revision, actor provenance, commit 시각과 정확히 해당 revision의 canonical body 또는 tombstone을 포함합니다. 서버는 metadata와 body를 같은 command transaction에 저장합니다. 로컬 replica가 revision-pinned resource와 batch 전체를 함께 commit한 뒤에만 다음 cursor를 확인합니다.

Handoff에는 project sequence 위에 actor projection이 적용됩니다. 인증된 sender와 receiver만 exact read, snapshot, metadata change, materialized change에서 해당 handoff와 immutable handoff_context packet을 볼 수 있습니다. 따라서 projection된 batch는 보이는 entry가 없어도 숨겨진 project sequence를 지나 cursor를 전진시킬 수 있습니다. Replica가 다른 actor의 handoff에서 반복하지 않도록 scan된 next_cursor를 그대로 신뢰합니다.

빈 exact-read replica는 GET .../snapshot으로 bootstrap합니다. 이 endpoint는 현재 resource 전체와 그 상태를 대표하는 project head를 하나의 SQLite read transaction에서 읽습니다. 이후 hydrated change만 적용하므로 cached resource가 durable cursor보다 앞설 수 없습니다. 첫 snapshot endpoint는 의도적으로 resource 10,000개로 제한하며, stable snapshot handle을 사용하는 pagination은 이후 scale-out 항목입니다. 과거 body가 없는 schema v3 change row는 현재 상태를 추정하지 않고 snapshot_required를 반환합니다. Replica 파일은 tenant, project, epoch, 인증 actor에 고정되며 actor profile을 바꾸려면 replica reset --force가 필요합니다. 기존 project-only replica는 actor가 미지정된 상태로 migration되므로 마찬가지로 명시적 reset이 필요합니다. 이는 sequence-consistent exact-read cache이며 아직 BM25/HNSW retrieval index는 아닙니다.

관리자가 기존 cursor를 무효화하는 방식으로 정본 history를 restore하거나 교체하면 project_epoch가 바뀝니다. Epoch가 다르면 pull은 fail-closed하고, operator가 replica reset --force를 실행한 뒤 다음 pull이 새 snapshot으로 bootstrap합니다. 서로 다른 history generation을 best-effort로 merge하지 않습니다.

Offline write는 암묵적인 multi-primary 시스템을 만들지 않습니다. Command ID와 base revision을 포함한 actor branch/outbox에 저장하고 명시적으로 publish합니다. 충돌은 구조화된 stale-revision 결과로 반환합니다.

Artifact namespace

Artifact subsystem은 cf-vfs와 JuiceFS의 유용한 경계를 차용합니다. 강한 일관성의 metadata와 immutable object body를 분리합니다.

/projects/<project>/sessions/<session>/canvas.md
/projects/<project>/sessions/<session>/artifacts/<name>
/projects/<project>/handoffs/<handoff>/request.json
/projects/<project>/handoffs/<handoff>/result.json
/projects/<project>/branches/<actor>/<segment>.jsonl
/projects/<project>/snapshots/<sequence>/manifest.json

작고 제한된 body는 metadata와 함께 inline으로 저장할 수 있습니다. 큰 body는 S3 호환 object storage의 immutable random generation을 사용합니다.

  1. 현재 mutation token과 만료 시간을 사용해 path를 reserve합니다.
  2. Object store에 직접 upload합니다.
  3. 서버가 관찰한 size, version, ETag와 선택형 digest를 검증합니다.
  4. Path token을 다시 확인하고 metadata를 원자적으로 publish합니다.
  5. 도달할 수 없는 generation을 idempotent garbage collection queue에 넣습니다.

Artifact layer는 POSIX open handle, lock, mmap, sparse write 또는 database-file 의미론을 보장하지 않습니다. AideMemo SQLite, redb, WAL, BM25, HNSW 파일은 이 원격 namespace를 통해 직접 열면 안 됩니다. 선택형 FUSE 또는 Python fsspec client는 공유 database volume이 아니라 materialized workspace를 노출합니다.

Artifact transport 및 garbage collection 결정

연구 snapshot은 2026-08-02이며 cf-vfs main의 69963db6072683ff030d629cfe3288ea565d6913을 검토했습니다. 차용할 부분은 Bash runtime이나 POSIX 형태 namespace가 아니라 opaque body lifecycle입니다. AideMemo는 이미 Rust로 구현한 더 작은 artifact 계약을 유지하고 다음 adapter 경계를 세 역할로 분리합니다.

역할소유하는 것소유하면 안 되는 것
Metadata coordinator인증된 scope, 논리 path, mutation token, reservation/verification lease, publication receipt, read retention, GC intent큰 body byte 또는 agent에게 반환되는 provider credential
Body store조건부 immutable create, HEAD, range/full read, idempotent delete, 선택형 multipart operationtenant authorization 또는 논리 path conflict resolution
Upload authority제한된 local proxy 또는 수명이 짧고 exact-key에 고정된 upload/download capabilitypublication 정본 또는 다른 object key를 선택할 권한

LocalArtifactStore는 이 의미론의 Phase-1 reference adapter이지 최종 trait 형태가 아닙니다. Portable protocol은 Rust domain type과 HTTP로 유지합니다. Cloudflare binding, S3 SDK, FUSE 또는 Python에 의존하는 계약으로 만들지 않습니다.

로컬 reference server는 임의의 논리 path를 URL에 직접 넣지 않고 opaque reservation ID를 사용합니다. Hosted adapter는 제한된 body 전송만 교체하고 같은 control-plane 형태를 유지합니다.

Route의미론
POST /v1/projects/{project}/artifact-reservationsWriter가 논리 path를 reserve하고 opaque generation token과 만료 시간을 받습니다.
PUT /v1/projects/{project}/artifact-reservations/{reservation}/body단일 노드/local 제한 업로드입니다. Hosted large body는 이 route를 통과하지 않습니다.
POST /v1/projects/{project}/artifact-reservations/{reservation}/upload-grantsS3 feature의 writer가 reservation보다 오래 지속되지 않는 conditional, exact-length/type single-PUT capability를 받습니다.
POST /v1/projects/{project}/artifact-reservations/{reservation}/publishCoordinator가 신뢰된 local observation 또는 S3 HEAD를 얻고 path token과 reservation을 다시 확인한 뒤 metadata를 원자적으로 publish합니다. Hosted publication에는 예상 size_bytes가 포함됩니다.
DELETE /v1/projects/{project}/artifact-reservations/{reservation}마지막 published path를 바꾸지 않고 abort하고 generation의 추후 삭제를 durable하게 예약합니다.
GET /v1/projects/{project}/artifacts/resolve?path=...Reader membership으로 현재 metadata를 resolve합니다.
POST /v1/projects/{project}/artifacts/{artifact}/downloads정확한 revision의 제한된 local body를 반환합니다.
POST /v1/projects/{project}/artifacts/{artifact}/download-grantsS3 feature의 reader에게 ETag/version 고정 GET capability를 주기 전에 정확한 현재 generation을 durable하게 retain합니다.

Catalog는 immutable body adapter의 credential-free identity digest도 저장합니다. Local storage는 repository layout에 고정되고 S3-compatible storage는 정확한 bucket, prefix, endpoint, signing region, addressing mode에 고정됩니다. 서버는 traffic을 받기 전에 불일치를 거부합니다. 따라서 이 값 중 하나를 바꾸려면 명시적인 artifact migration 또는 비어 있는 별도 --artifact-root가 필요하며, in-place backend switch로 해석하지 않습니다.

모든 control-plane call에서 bearer binding이 tenant와 actor identity를 제공합니다. Reader는 resolve/download, writer는 reserve/upload/publish/abort할 수 있습니다. Upload Local reference는 reservation expiry 또는 publication 후 24시간 동안 exact reservation/publication replay receipt를 유지한 뒤 bounded GC pass에서 정리합니다. 이 기간에는 이후 replacement와 GC로 원래 body가 삭제됐어도 publish retry가 최초 reference를 반환합니다. capability 자체도 bearer credential입니다. 수명이 짧고 provider가 지원하는 범위에서 하나의 random generation key, method, content type, expected size/checksum, expiry, conditional create에 고정합니다. 이를 log에 남기거나 artifact record에 저장하지 않습니다. Presigned URL은 만료 전 재사용할 수 있으므로 one-shot 보장이 아닙니다. Publication에는 여전히 immutable key, 신뢰된 HEAD/checksum observation, 논리 path compare-and-swap이 모두 필요합니다. 조건부 single 및 multipart completion 지원은 모든 S3-compatible 제품에 있다고 가정하지 않고 adapter conformance 항목으로 검증합니다.

R2의 직접 Workers 및 S3 API는 object write, read, delete, list에 strong consistency를 제공합니다. Cache가 활성화된 custom-domain response는 이 보장에 포함되지 않으며 publish 검증에 사용하면 안 됩니다. Hosted upload 검증은 binding 또는 S3 API를 직접 사용합니다. Portable hosted 첫 slice는 single PUT이며 공통 S3/R2 5 GB 경계로 제한됩니다. Multipart는 별도 향후 경로이고 신뢰된 completion만 observed generation을 생성할 수 있습니다.

2026-08-02에 확인한 공식 R2 S3 compatibility tableIf-None-Match를 포함한 conditional PutObject를 지원하며, presigned URL contract은 만료 전까지 재사용 가능한 exact-key PUT/GET grant를 지원합니다. 따라서 feature-gated Rust adapter는 If-None-Match: *, 정확한 content length/type, generation metadata를 signing하고 URL을 redacted bearer capability로 취급합니다. 또한 trusted HEAD의 size/generation/ETag를 확인하고 signed GET이 coordinator에 저장된 read retention보다 오래 유지되는 것을 거부합니다. R2는 이에 대응하는 conditional DeleteObject header를 문서화하지 않으므로 delete는 random generation key를 절대 재사용하지 않는 더 강한 AideMemo invariant에 의존합니다.

Garbage collection은 bucket listing이 아니라 metadata로 구동합니다.

  1. Replacement, abort, expiry, verification 실패 또는 publication CAS 상실 시 generation을 unreachable하게 만드는 같은 metadata transaction에서 durable GC candidate 하나를 기록합니다.
  2. not_before는 upload-capability expiry와 settlement grace의 합보다 이르지 않고, 마지막으로 부여한 download retention보다도 이르지 않습니다. 늦은 PUT이 방금 삭제한 object를 재생성하거나 활성 signed download가 body를 잃는 것을 막습니다.
  3. 제한된 worker가 due candidate를 lease한 뒤 published path, live reservation 또는 read retention이 exact generation/version을 참조하지 않는지 다시 확인합니다.
  4. 제한된 batch로 idempotent exact-key delete를 실행합니다. 성공하면 candidate를 제거하고 실패하면 attempt, error, exponential retry time을 기록합니다.
  5. 느린 reconciliation sweep이 adapter-owned object prefix와 catalog reachability를 비교할 수 있지만 listing은 repair evidence일 뿐 canonical liveness가 아닙니다.

같은 table/queue 구현은 단일 노드 서버 또는 Kubernetes worker에서 실행할 수 있습니다. Cloudflare profile에서는 project 단위 Durable Object가 짧은 metadata transaction을 소유하고 하나의 alarm으로 가장 이른 expiry/GC retry를 예약할 수 있습니다. PostgreSQL은 초기 hosted canonical adapter로 유지하며 Durable Object를 global singleton이나 PostgreSQL 옆의 두 번째 암묵적 writer로 만들면 안 됩니다.

PyO3는 storage-server 경계가 아닙니다. 기존 Rust/Python binding이 추후 fsspec-compatible materialization client를 노출할 수 있지만 upload, publication, conflict 의미론은 동일한 인증 HTTP protocol을 사용합니다. 별도 PyO3 VFS는 authorization, CAS, retry, GC를 중복 구현하고 Workers, Node 또는 Kubernetes client에는 도움이 되지 않습니다.

구현 gate는 failure-oriented합니다.

  • Reservation, upload, verification claim, metadata commit, object delete 직후 crash
  • 정확한 reserve/upload/publish retry와 changed-body 또는 changed-command 재사용 구분
  • Abort/expiry 이후 늦은 upload와 동일 path의 concurrent replacement
  • Digest, size, ETag/version, tenant, project, actor role, object prefix mismatch
  • Signed-download retention과 replacement/GC race
  • 영구 실패 delete에서 bounded batching/backoff
  • Local filesystem, R2, AWS S3, 선택한 on-premises S3-compatible 구현에 같은 lifecycle suite 적용

Local authenticated HTTP와 durable GC 구간, feature-gated S3/R2 server wiring은 구현됐습니다. Hosted 경로는 writer-only upload grant를 발급하고 신뢰된 HEAD만 publish하며, reader GET을 signing하기 전에 read retention을 저장하고 같은 durable GC intent를 exact-generation provider delete로 처리합니다. Disposable local MinIO process는 ./scripts/artifact-s3-minio-conformance.sh를 통한 실제 presigned HTTP lifecycle을 통과했습니다. Managed R2/AWS 실행은 남아 있으며 이후 multipart/resume, 마지막으로 선택형 project Durable Object coordinator를 추가합니다.

검색 일관성

Fact와 graph 레코드가 정본이며 lexical/vector index는 재구축 가능한 projection입니다. 모든 index는 적용한 가장 높은 project sequence를 보고합니다.

읽기일관성
Resource ID의 exact get정본 record transaction
Handoff status 또는 claim정본 record transaction
Search/query/context파생 index + index_seq watermark
로컬 offline search마지막 적용 replica sequence

기본 search는 eventual consistency일 수 있지만 호출자는 at_least_seq를 요청할 수 있습니다. 서버는 제한된 deadline 동안 기다리고, 가능한 경우 정본 lexical 경로로 폴백하거나 명시적인 not-ready 상태를 반환합니다. Index가 뒤처졌는데 read-your-writes를 조용히 주장하면 안 됩니다.

배포 profile

단일 노드 서버

첫 실행 가능한 server profile은 SQLite metadata를 유지하고 기본적으로 local artifact body를 사용합니다. Feature-gated process는 같은 catalog에 S3/R2/MinIO body를 대신 연결할 수 있습니다. 애플리케이션 replica를 정확히 하나만 지원하며 고가용성을 주장하지 않고 원격 identity, command, change feed, artifact lifecycle, local cache 계약을 검증합니다.

제한된 기반은 이제 workspace에서 실행할 수 있습니다. Password가 아닌 높은 entropy의 bearer token을 생성하고 token file을 비공개로 유지한 뒤 활성 membership 하나를 bootstrap합니다.

openssl rand -hex 32 > /secure/aidememo-writer.token
chmod 600 /secure/aidememo-writer.token

cargo run -p aidememo-server -- bootstrap \
--database /data/aidememo-ssot.sqlite \
--tenant-id acme \
--project-id memory \
--actor-id codex-p1 \
--token-file /secure/aidememo-writer.token

cargo run -p aidememo-server -- serve \
--database /data/aidememo-ssot.sqlite \
--artifact-root /data/aidememo-artifacts

--artifact-root를 생략하면 서버는 <database>.artifacts를 사용합니다. R2 body store에서도 이 경로는 별도 metadata/GC catalog로 유지하고 credential은 command-line argument가 아니라 표준 AWS provider chain으로 전달합니다.

AWS_ACCESS_KEY_ID="$R2_ACCESS_KEY_ID" \
AWS_SECRET_ACCESS_KEY="$R2_SECRET_ACCESS_KEY" \
cargo run -p aidememo-server --features s3 -- serve \
--database /data/aidememo-ssot.sqlite \
--artifact-root /data/aidememo-artifact-catalog \
--artifact-backend s3 \
--artifact-s3-bucket aidememo \
--artifact-s3-region auto \
--artifact-s3-endpoint https://ACCOUNT_ID.r2.cloudflarestorage.com

Local MinIO처럼 path-style addressing이 필요한 provider에서는 --artifact-s3-force-path-style을 사용합니다. Capability URL은 bearer credential이므로 log에 남기면 안 됩니다.

Bootstrap은 SHA-256 token digest만 저장하고 재시도 시 기존 project epoch를 재사용합니다. 처음 저장한 label과 timestamp를 유지하며 epoch, actor kind, membership role, token 소유권 충돌은 fail closed로 처리합니다. 서버는 기본적으로 127.0.0.1:3030에 binding합니다. Loopback이 아닌 plaintext binding은 --allow-insecure-http를 명시하지 않으면 거부되며 프로덕션 bearer traffic에는 여전히 TLS ingress가 필요합니다.

현재 HTTP surface는 의도적으로 작습니다.

Endpoint계약
GET /healthProcess mode와 SQLite schema version
GET /v1/projects/{project}/identityBearer에 binding된 tenant, project, actor와 활성 membership role 확인
POST /v1/commands인증된 custom.* resource.put / resource.delete, idempotent receipt, revision CAS
GET /v1/projects/{project}/resources/{kind}/{id}정확한 정본 body 또는 tombstone. Handoff와 context는 sender/receiver에게만 노출
GET /v1/projects/{project}/changesEpoch/sequence cursor 이후 순서가 있는 metadata-only change entry
GET /v1/projects/{project}/changes/materialized각 revision의 정확한 canonical body 또는 tombstone을 포함한 순서형 change
GET /v1/projects/{project}/snapshot현재 상태 전체와 이를 대표하는 project head의 원자적 bounded bootstrap
POST /v1/projects/{project}/sessionsTyped session 하나를 생성하고 source_id를 고정
POST /v1/projects/{project}/facts기존 session에 fact를 생성하며 source와 actor는 서버에서 상속
POST /v1/projects/{project}/handoff-contexts정확한 handoff route에 고정된 immutable bounded sender packet 생성
POST /v1/projects/{project}/handoffsSession pointer를 다른 활성 writer에게 전달
GET /v1/projects/{project}/handoffs?box=inbox|outbox인증 actor의 indexed mailbox, 선택형 source_id, include_completed, before_seq, 제한된 limit
POST .../handoffs/{id}/acceptexpected_revision과 exclusive claim_id로 claim
POST .../handoffs/{id}/returnClaim과 결과 fact의 session/source/actor를 검증하고 outcome 반환
GET .../handoffs/{id}송신자/수신자 전용 typed status
POST /v1/projects/{project}/artifact-reservationsWriter 전용 idempotent logical-path reservation
PUT .../artifact-reservations/{reservation}/bodyWriter 전용 direct local upload, 최대 64 MiB
POST .../artifact-reservations/{reservation}/upload-grantsS3 feature의 writer 전용 conditional single-PUT capability
POST .../artifact-reservations/{reservation}/publishLocal byte 또는 신뢰된 S3 HEAD를 다시 관찰하고 예약 generation을 원자적으로 publish
DELETE .../artifact-reservations/{reservation}현재 path를 교체하지 않고 abort한 뒤 eventual deletion queue 기록
GET /v1/projects/{project}/artifacts/resolve?path=...Reader가 볼 수 있는 현재 artifact metadata
POST .../artifacts/{artifact}/downloadsReader가 볼 수 있는 exact-revision local body download
POST .../artifacts/{artifact}/download-grantsS3 feature의 reader 전용 retained exact-generation GET capability

생성 요청은 {"command_id":"...","payload":{...}}를 사용합니다. 상태 전이는 클라이언트가 관찰한 revision도 전달합니다.

{
"command_id": "command_accept_01",
"expected_revision": 1,
"payload": {"claim_id": "worker_attempt_01"}
}

생성 요청은 정확히 같은 body를 전송 재시도할 수 있도록 resource ID에서 안정적인 command ID를 만듭니다. 수신자 전이는 인증 actor와 시도 횟수에서 claim을 만들고, claim과 정확한 결과 근거에서 return command를 만듭니다. CLI는 전송 오류 뒤 동일한 POST body를 한 번 재시도합니다. 서버는 변경 가능한 handoff 상태를 다시 읽기 전에 기존 receipt를 검증하고 replay하며, 이후 CLI/MCP 실행도 이미 적용된 정확한 accept 또는 return을 인식해 recovered: true를 반환합니다. 다른 actor는 최초 actor의 command ID를 replay할 수 없습니다.

이 보장은 resource 단위입니다. 새로운 send 실행은 현재 새 handoff/context 쌍을 만들기 때문에 새 assignment를 의미하며, 응답이 불확실했던 이전 실행을 중복 제거하지 않습니다. Client operation key 또는 offline outbox가 생기기 전에는 send를 무작정 다시 실행하지 말고 sender outbox에서 handoff ID를 찾아 이어갑니다.

Mailbox actor identity는 항상 bearer binding에서 가져오며 actor_id query parameter는 거부합니다. 결과는 최신순이고 각 handoff의 현재 resource revision과 최신 project_seq를 포함합니다. 다음 page가 있으면 next_before_seq가 다음 요청의 exclusive cursor입니다. Inbox는 기본적으로 completed 작업을 제외하고 outbox는 기본적으로 포함합니다. SQLite schema v3 mailbox index는 정본 handoff 상태, receipt, change, audit row와 같은 transaction에서 갱신됩니다. V2 ledger를 열면 정본 handoff resource와 최신 change sequence에서 index를 backfill합니다.

보호된 모든 요청은 bearer 값을 hash하고 저장된 tenant와 actor를 찾은 뒤 활성 project membership을 다시 읽습니다. Exact resource, snapshot, change feed 응답은 typed status route와 동일한 sender/receiver visibility를 handoff와 context packet에 적용합니다. Command JSON은 deny_unknown_fields를 사용하므로 body의 tenant 또는 actor identity를 무시하지 않고 거부합니다. 정본 resource body, receipt, resource revision, project sequence, change entry, audit row는 한 SQLite transaction으로 commit됩니다.

이 process는 application replica 하나를 지원하며 built-in TLS, token rotation/revocation command, rate limit, PostgreSQL 정본 backend, 고가용성 coordination은 아직 제공하지 않습니다. Phase 1에는 인증된 sequence-aware lexical/hybrid retrieval, handoff lease heartbeat와 stale-claim recovery, stateless HTTP MCP gateway, sequence-consistent exact-read replica, durable explicit remote-send outbox가 포함됩니다. 로컬 replica 자체는 여전히 BM25/HNSW retrieval-index replica가 아닙니다. 별도 local artifact repository는 인증된 reader/writer route에 연결됐고 idempotent reservation, immutable upload, 신뢰 가능한 SHA-256/size 재관찰, CAS publication, exact-revision read, abort, 재시작에 안전한 durable GC를 검증합니다. Direct body는 64 MiB로 제한되며 향후 hosted streaming 계약은 아닙니다. CLI와 stdio MCP는 named connected handoff profile을 지원하고 client는 별도 exact-read replica를 유지할 수 있지만 일반 remote storage backend는 아닙니다. Typed fact는 계속 정본 result evidence이며 서버의 lexical 및 semantic/HNSW projection은 정본 project sequence에 고정되고 언제든 재구축 가능한 derived state입니다. 서버 계약 실행 파일이지 출시된 SaaS나 aidememo mcp-serve의 대체물이 아닙니다.

Cloudflare edge 호스팅

이식 가능한 호스팅 profile은 Worker를 TLS, 인증, limit, routing에 사용합니다. Hyperdrive는 Worker 또는 origin service를 PostgreSQL에 연결할 수 있고 R2는 S3 artifact 계약을 구현합니다. Project별 Durable Object는 활성 WebSocket presence, 짧은 lease 또는 경쟁이 심한 session/handoff coordination을 소유할 수 있습니다. Durable state는 계속 project 범위입니다.

향후 Cloudflare-native 정본 adapter는 project 레코드와 change log를 하나의 SQLite-backed Durable Object에 배치할 수 있습니다. Logical snapshot/export, restore, tenant deletion, version 간 migration, storage conformance를 제공하기 전에는 SSOT backend라고 부르지 않습니다.

Kubernetes 및 온프레미스

프로덕션 chart는 애플리케이션 pod를 교체 가능하게 유지합니다.

aidememo-api Deployment
aidememo-indexer Deployment
aidememo-migrate Job
aidememo-gc CronJob
PostgreSQL external or operator-managed
S3-compatible external

프로덕션 기본값은 사용자가 제공하는 PostgreSQL과 S3 호환 저장소입니다. 개발용 values file은 단일 노드 의존성을 설치할 수 있지만 고가용성 profile은 아닙니다. API replica는 read-write-many volume을 통해 live embedded SQLite 파일을 공유하지 않습니다.

코드 경계

여섯 기반 crate가 존재하며 같은 경계 map에 다음 planned canonical adapter도 표시합니다.

aidememo-domain portable ID, command, record, invariant
aidememo-service command/query orchestration과 authorization context
aidememo-store-local SQLite command ledger와 transactional handoff index
aidememo-client 인증 transport와 격리된 exact-read replica
aidememo-artifacts local lifecycle과 선택형 S3/R2 direct-transfer adapter
aidememo-store-postgres planned 서버 정본 adapter
aidememo-server 제한된 인증 HTTP resource/change/handoff surface

aidememo-domain은 native model과 filesystem 가정이 없어야 하며 invariant test를 local, PostgreSQL, 선택형 Durable Object adapter에 공통 실행할 수 있어야 합니다. 기존의 큰 동기식 StoreBackend는 embedded 구현 경계로 남깁니다. 원격 HTTP backend가 로컬 Path로 여는 store인 것처럼 동작해서는 안 됩니다.

aidememo-domain은 검증된 tenant, project, actor, membership, command, revision, audit, change-feed, tombstone, artifact reference, typed session/fact, handoff 상태 machine type을 제공합니다. 모든 lookup과 feed batch는 tenant-project 복합 scope를 가집니다. aidememo-service는 인증 identity와 membership을 untrusted envelope에 결합하고 JSON field를 재귀적으로 canonicalize하여 command fingerprint를 계산합니다. aidememo-store-local은 기존 embedded store와 분리된 SQLite database에서 receipt, resource revision, change, audit, project sequence, actor-relative handoff index를 한 transaction으로 저장합니다. aidememo-server는 token binding과 membership을 그 ledger에 저장하고 request body 밖에서 identity를 결정하며, loopback 우선 Axum process로 bootstrap, exact resource read, extension resource command, typed session/fact/handoff와 mailbox route, change feed, health를 노출합니다. aidememo-client는 이 route에 인증하고 별도 SQLite scope/epoch cursor와 exact canonical resource cache를 유지하며 fully materialized change batch를 원자적으로 적용합니다. Scope 또는 epoch가 바뀌면 명시적 reset을 요구하며 embedded search store를 열거나 재해석하지 않습니다. aidememo-artifacts는 별도 SQLite logical-path catalog와 immutable generation file을 유지합니다. Replacement에는 현재 published mutation token이 필요하고, live 경쟁 reservation을 거부하며, local publication 전에 byte를 다시 hash하고, abort 시 이전 version을 보존하며, logical artifact path를 OS path로 해석하지 않습니다. Replacement, abort, expired reservation은 durable exact-generation GC intent를 쓰고, leased bounded worker는 liveness를 다시 검사한 뒤 idempotent delete와 failure backoff를 수행합니다. 직접 local upload는 64 MiB로 제한됩니다. s3 feature는 검증된 provider config, credential-chain loading, conditional presigned single-PUT, trusted HEAD, read retention 범위의 exact GET grant, bounded exact read, immutable-key delete를 제공합니다. Presigned capability의 Debug 출력에서는 URL을 redact합니다. Server feature는 이 capability를 인증된 writer/reader route에 연결하고, trusted hosted observation에서만 nullable digest를 허용하며, GET signing 전에 read retention을 저장하고 durable GC queue에서 provider delete를 실행합니다. Ignored provider test와 local MinIO harness는 실제 S3-compatible process에서 conditional presigned PUT, replay 거부, trusted HEAD, presigned/SDK exact GET, idempotent delete를 검사합니다. Managed R2/AWS conformance와 multipart transfer는 아직 열려 있습니다.

Backend 중립 conformance::run fixture는 정확한 idempotent receipt replay, command ID 충돌, stale revision 거부, 단조 증가 project sequence, 삭제 tombstone, fail-closed epoch 변경, 정본 이력보다 앞선 cursor 거부를 검사합니다. In-memory reference와 실제 SQLite adapter가 모두 통과합니다. SQLite integration test는 process reopen, 두 concurrent connection의 duplicate submission, 두 tenant 아래 같은 project ID 격리도 검증합니다. HTTP test는 누락·미등록 bearer 거부, identity field injection, writer replay/conflict, reader 전용 sync, role 강제와 codex-p1 -> codex-p2 -> Hermes typed handoff chain도 검사합니다. Binary 수준 test도 URL 하나에 bearer profile 두 개를 저장하고 CLI와 설치된 stdio MCP 모두에서 send/inbox/accept/return/outbox codex-p1 -> codex-p2 흐름을 완료한 뒤 exact-read replica를 bootstrap하고 서버 종료 후 완료 handoff를 읽으며 guarded reset도 검사합니다. PostgreSQL과 Durable Object 정본 adapter는 아직 연결되지 않았습니다. 단일 노드 profile에는 sequence-aware lexical/hybrid retrieval, stateless 인증 HTTP MCP gateway, exact-read replica, lease-aware handoff recovery, explicit durable remote-send outbox가 연결됐습니다. Artifact HTTP test는 reader/writer authorization, exact reservation과 publication replay, 변경된 request reuse, revision-pinned local download, hosted upload/download grant, durable read retention, replacement, abort, expiry, mock provider를 통한 local/S3 garbage collection을 검사합니다. Ignored provider test와 local MinIO harness는 실제 S3-compatible process에서 conditional presigned PUT, replay 거부, trusted HEAD, presigned/SDK exact GET, idempotent delete를 검사합니다. Managed R2/AWS conformance와 multipart transfer는 아직 열려 있습니다. 여섯 기반 crate는 server-facing 공개 API와 release 순서를 승인할 때까지 모두 publish = false이며 기존 v0.1.0 crate 배포 흐름에 조용히 포함되지 않습니다.

단계별 delivery gate

Phase 0 — 서버 계약 고정

  • Tenant, project, membership, actor, command, revision, change, audit, artifact-reference schema를 추가합니다.
  • Error code와 cursor/epoch 동작을 명세합니다.
  • Backend 중립 conformance fixture를 추가합니다.
  • 현재 로컬 API와 파일 format을 보존합니다.

종료 gate: 두 독립 client가 identity를 재정의할 수 없고, 중복 command submission이 mutation 하나만 만들며, stale revision이 실패하고, 삭제가 tombstone으로 replica에 도착합니다.

현재 상태: 기존 embedded API나 파일 format을 변경하지 않고 별도 SQLite adapter와 인증 HTTP test가 Phase 0 code 종료 gate를 통과합니다. 제한된 aidememo-server 실행 파일은 workspace 전용이며 미배포 상태지만, named CLI와 stdio MCP profile은 typed handoff surface를 실제로 사용합니다.

Phase 1 — 단일 노드 원격 SSOT

  • SQLite database 하나와 로컬 artifact directory 위에서 서비스를 실행합니다.
  • CLI와 MCP 설치가 인증된 원격 profile 하나를 사용하게 합니다.
  • 로컬 read-cache bootstrap, incremental pull, reset, offline outbox를 추가합니다.

종료 gate: Codex primary, Codex secondary, Hermes가 하나의 원격 project를 통해 handoff를 완료합니다. 서버가 중단되면 cache read는 유지되지만 조용한 multi-primary write는 만들지 않습니다.

현재 상태: main의 제한된 단일 노드 engineering profile에서 완료됐습니다. Scenario T는 project 하나와 동일 session/source 경계에서 Codex A -> 원격 SSOT -> Codex B -> Hermes 흐름을 실행하면서 post-commit response loss와 서버 outage를 의도적으로 주입합니다. 이 gate는 retry 뒤 duplicate canonical resource와 handoff가 0개 추가되고, stale revision과 비참여자 read가 fail-closed하며, offline에서도 cached exact read가 유지되고, queued send가 서로 다른 CLI process 사이에서 살아남아 복구 뒤 명시적 publish에서만 정본에 반영되며, at_least_seq retrieval이 commit된 결과 sequence에 도달하고, 만료된 worker가 더 새로운 claim 뒤에 return할 수 없음을 검증합니다. 최종 Hermes evidence는 원래 session/source와 인증 actor 경계에 그대로 연결됩니다.

replica pull --remote-profile은 exact-read cache의 인증 bootstrap 및 incremental pull 진입점으로 유지됩니다. 완료된 Phase 1 surface는 제한된 SQLite SSOT ledger, 선택형 S3-compatible body를 가진 local artifact lifecycle, bearer binding을 사용하는 named CLI와 stdio MCP profile, 인증된 stateless HTTP MCP gateway, sequence-aware lexical 및 선택형 semantic/HNSW retrieval projection, lease-aware handoff heartbeat/recovery, sequence-consistent exact-read replica, durable explicit remote-send outbox로 구성됩니다. Projection과 cache는 계속 정본 record와 project sequence의 하위 derived state입니다.

이로써 Phase 1 engineering 종료 gate를 닫습니다. 그러나 workspace server가 production SaaS 계약으로 승격된 것은 아닙니다. PostgreSQL 정본 storage, 고가용성, production TLS/rate-limit/token-rotation 운영, managed R2/AWS conformance, backup/restore drill, multi-replica deployment는 Phase 2 이후 작업으로 남습니다. PostgreSQL 구현은 이 gate 이후에 시작합니다.

Phase 2 — 이식 가능한 프로덕션 backend

  • PostgreSQL을 추가하고 연결된 S3 호환 artifact lifecycle을 managed R2/AWS S3와 선택한 production on-premises 구현에서 conformance 검증합니다. Disposable local MinIO profile은 이미 opt-in lifecycle harness를 통과합니다.
  • Transactional outbox indexer와 sequence watermark를 추가합니다.
  • Logical backup/restore 및 tenant export/delete 훈련을 추가합니다.

종료 gate: concurrent claim/return, restore, replica rebuild, tenant isolation, index rebuild suite가 SQLite와 PostgreSQL 모두에서 통과합니다.

Phase 3 — Cloudflare 호스팅 profile

  • Worker gateway, Hyperdrive/R2 설정, 선택형 active-project Durable Object를 추가합니다.
  • Durable Object를 project 간 global query 경로에서 제외합니다.
  • 로컬 benchmark 주장을 가져오지 않고 한국 지역 end-to-end latency, cold start, object operation, index lag를 측정합니다.

종료 gate: 호스팅 결과가 동일한 conformance suite를 통과하고 측정된 cost, latency, recovery, region placement 경계를 문서화합니다.

Phase 4 — Kubernetes 배포판

  • 외부 PostgreSQL/S3 기본값을 가진 Helm chart를 배포합니다.
  • Migration, network policy, disruption budget, observability, backup, restore, rolling-upgrade test를 추가합니다.
  • Compatibility matrix와 air-gapped 설치 경로를 배포합니다.

종료 gate: 깨끗한 cluster 설치, upgrade, node disruption, database restore, 완전한 tenant export/import를 문서 명령으로 재현할 수 있습니다.

비목표

  • 임의 애플리케이션을 위한 분산 POSIX filesystem.
  • R2, FUSE, 원격 VFS에서 SQLite, WAL, redb, BM25, HNSW 파일 열기.
  • 외부 side effect의 exactly-once. 서비스는 idempotent command receipt와 at-least-once outbox를 제공합니다.
  • Offline writer를 위한 숨겨진 conflict resolution.
  • actor_id, agent alias 또는 source_id를 인증으로 취급하기.
  • Worker exit, artifact upload 또는 handoff delivery를 task 성공 증거로 취급하기.

참고 자료