Actions
이슈 #433
진행중
JL
HP
VfsService 포팅 — per-account 가상 파일시스템 (Read/Write/List/Mkdir/Delete/Stat/Exists/Lock/Unlock)
이슈 #433:
VfsService 포팅 — per-account 가상 파일시스템 (Read/Write/List/Mkdir/Delete/Stat/Exists/Lock/Unlock)
시작일:
2026/05/11
완료일:
진척도:
0%
추정시간:
설명
배경¶
alphea-ai-edge (Rust) 에 legacy VfsService 9 RPC 를 포팅한다. legacy 구현은 wallet-keyed vfs_nodes 테이블 + in-memory MemLocker 로 단순한 per-account 가상 파일시스템을 제공.
레거시 위치:
- proto:
~/Works/alphea-rpc-next/alphea-rpc/proto/alphea/v1/vfs.proto(9 RPC) - handler:
~/Works/alphea-rpc-next/alphea-rpc/internal/handler/stubs.go:51-330(~280 LoC) - store:
~/Works/alphea-rpc-next/alphea-rpc/internal/store/vfs.go(231 LoC) - domain locker:
~/Works/alphea-rpc-next/alphea-rpc/internal/domain/vfs/{locker.go,memlocker.go}(123 LoC)
목표¶
- 9 RPC 모두 작동:
Read,Write,List,Mkdir,Delete,Stat,Exists,Lock,Unlock. - legacy 와 기능 동등하지만 account-scope (user_id UUID) 로 전환 — wallet_address 키는 사용 안 함 (#421 정합성).
- in-memory locker 백그라운드 evictor 포함.
-
/modules,/sysread-only 경로 보호. - 처음 접근 시 default tree 자동 초기화 (
/home/node/{jobs,logs,workspace},/modules/demo.*,/sys/version등).
Proto 처리¶
proto/alphea/v1/vfs.proto 를 legacy 에서 복사하되:
-
option go_package라인 제거 (CLAUDE.md 규칙). - 기존 필드명
wallet_address는 wire 호환성을 위해 그대로 유지. 핸들러는 이 필드를 무시하고 JWTcaller_user_id를 사용 (silent override) — 향후 정리 시 별도 이슈에서user_id로 rename. -
build.rs에 vfs.proto 추가.
DB 스키마 (#254 refs)¶
신규 마이그레이션 migrations/0014_vfs_nodes.sql:
CREATE TABLE vfs_nodes (
id BIGSERIAL PRIMARY KEY,
user_id UUID NOT NULL,
path TEXT NOT NULL,
name TEXT NOT NULL,
node_type TEXT NOT NULL, -- 'file' | 'directory'
content TEXT,
mime_type TEXT,
size BIGINT NOT NULL DEFAULT 0,
readonly BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
modified_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (user_id, path)
);
CREATE INDEX idx_vfs_user_path ON vfs_nodes (user_id, path);
(legacy 의 BIGINT unix-seconds 타임스탬프 → Rust 측 TIMESTAMPTZ 통일. proto wire 에서는 .timestamp() 로 변환.)
인증¶
-
auth_middleware::public_path_set()에 VfsService RPC 추가하지 않음 — 모두 protected. - 핸들러 본문에서
require_user_token(&request)?로 GoTrue user UUID 가져와user_id로 사용. - proto 의
wallet_address필드는 받아서 무시 (deprecated). - node 토큰은 거부 — VFS 는 사용자 계정 스코프 자원.
Lock 의미¶
-
holder_id는 클라이언트가 정하는 자유 문자열 (legacy 와 동일 — node_id, session id, 임의 UUID 모두 가능). - TTL 기본 60초, 커스텀 TTL 지정 가능.
- 동일 holder 의 재 lock 은 TTL 연장.
-
Write/Delete핸들러는 metadatax-holder-id(gRPC 소문자 헤더) 로 lock ownership 검증 — holder 가 다른 사람의 lock 키와 매치되면aborted반환.
파일 추가¶
-
proto/alphea/v1/vfs.proto(copy + go_package 제거) migrations/0014_vfs_nodes.sql-
src/store/vfs.rs—VfsRow,get_node,list_children,write,mkdir,delete,exists,ensure_init -
src/domain/vfs/mod.rs+src/domain/vfs/locker.rs—MemLocker(tokio::sync::Mutex<HashMap>기반, evictor task) -
src/handler/vfs.rs— 9 RPC
파일 수정¶
-
build.rs— proto 목록에 vfs.proto 추가 -
src/handler/mod.rs—VfsHandler노출 -
src/server/mod.rs—AppState.vfs_locker: Arc<MemLocker>추가, evictor spawn, gRPC 서비스 등록 -
src/main.rs— AppState 구성 -
src/store/mod.rs—pub mod vfs; -
src/domain/mod.rs—pub mod vfs;
완료 기준 (DoD)¶
- 9 RPC 모두 구현 (unimplemented stub 없음).
-
cargo buildclean. -
cargo clippy --lib신규 코드 경고 0. -
cargo test --lib전부 통과 + 신규 단위 테스트:-
vfs::locker::*(acquire / re-acquire same holder / blocked by other / TTL expiry / unlock). -
vfs::store::*(path 정규화 / list_children direct-child filter — 가능하면).
-
- CLAUDE.md 의 "Current Implementation Snapshot" 표에 VfsService 추가, "5 미포팅 서비스" 목록에서 제거.
관련 이슈¶
- #254 — DB 스키마 (마이그레이션 추가 시 refs).
- #421 — account-scope 전환. 본 이슈가 그 정책 위에서 동작.
-
#375 — JWT 전역 인터셉터.
require_user_token사용.
예상 작업량¶
- proto + migration + store: 1.5h
- locker + evictor: 1h
- handler 9 RPC: 2h
- 테스트 + wiring + CLAUDE.md: 1.5h
- 총: 6h
Actions