store · personal second brain backend

store

다수 사람·LLM 에이전트가 공유하는 노트·개념·관계 그래프 백엔드. PostgreSQL 16 + Apache AGE + pgvector 한 인스턴스에 모두 들어가고, 모든 데이터는 REST API 로만 다룹니다. account 단위로 그래프가 분리되어 다른 사람의 데이터에 절대 접근할 수 없습니다.

basehttps://store.parklab.work authBearer <token> formatJSON · UTF-8 tlsLet's Encrypt
목차
  1. 무엇을 하는가
  2. 인증과 토큰
  3. 노드 CRUD
  4. account 격리
  5. 관리자 엔드포인트
  6. 헬스체크
  7. 현재 한계와 다음

01무엇을 하는가

store 는 LLM 이나 사람이 만든 노트·개념·아이디어를 영구 저장하고, 노드 사이의 관계를 그래프로 연결하는 두뇌 보조 서비스입니다. 단일 도메인 https://store.parklab.work 에서 REST API 로 동작하며, 다음을 약속합니다.

02인증과 토큰

모든 사용자 엔드포인트(/v1/nodes 등)는 HTTP 헤더 Authorization: Bearer <token> 가 필요합니다. 토큰은 32바이트 임의 비밀의 base64url 표현(약 43자)이며, 발급 시 한 번만 응답에 노출됩니다. 잃어버리면 새로 발급받아야 합니다.

토큰 발급 응답에는 평문 token 외에 prefix(앞 12자)가 함께 들어 있습니다. prefix 는 평문 비밀이 아니므로 운영 로그·감사·티켓에 안전하게 적을 수 있고, 같은 account 가 여러 토큰을 가진 경우 이들을 구분하는 유일한 안전 식별자입니다 (DB 에 저장된 prefix 와 같음).

일반 사용자 — 토큰 받기

관리자(ADMIN_TOKEN 보유자)에게 다음을 요청합니다.

  1. 본인용 service account 생성
  2. 해당 account 에 발급한 Bearer 토큰 전달

관리자는 관리자 엔드포인트 절차로 발급해 줍니다. 받은 토큰은 한 번만 화면에 보이는 plaintext 이므로 즉시 안전한 곳에 보관하세요.

토큰 사용

export STORE_TOKEN='<발급받은_토큰>'

curl -fsS https://store.parklab.work/v1/nodes \
  -H "Authorization: Bearer $STORE_TOKEN"
⚠ 보안 · 토큰은 git, 채팅, 로그 어디에도 평문으로 남기지 말고 환경변수나 비밀 매니저에 두세요. 노출됐다고 의심되면 관리자에게 즉시 회수(revoke) 요청.

03노드 CRUD

노드 한 개는 노트·개념·엔터티 등 어떤 단위든 됩니다. kindtitle 은 필수, 나머지는 선택입니다. 모든 노드는 호출자 account 의 AGE 그래프 안에 동시에 vertex 로 만들어집니다.

노드 생성 — POST/v1/nodes

curl -fsS -X POST https://store.parklab.work/v1/nodes \
  -H "Authorization: Bearer $STORE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "Note",
    "title": "첫 노드",
    "body": "store 에 처음 저장한 노드.",
    "meta": { "tags": ["intro"] }
  }'

응답 (예시)

{
  "id": "a12f8457-e553-44a2-916e-5f89a5fd7569",
  "account_id": "49629210-9611-4d96-a067-dbb030fd4568",
  "kind": "Note",
  "title": "첫 노드",
  "body": "store 에 처음 저장한 노드.",
  "meta": { "tags": ["intro"] },
  "created_at": "2026-05-07T15:16:41.370414Z",
  "updated_at": "2026-05-07T15:16:41.370414Z"
}

단건 조회 / 목록 / 패치 / 삭제

endpoint설명응답
POST /v1/nodes새 노드 생성. Content-Type: application/json 필수.201 + node JSON · 400 잘못된 body · 415 잘못된 Content-Type
GET /v1/nodes/{id}한 노드 조회. account 의 노드가 아니면 404.200 + node JSON · 400 잘못된 id · 404 없음
GET /v1/nodes?limit=N&offset=M최신순. limit 1..200 (기본 50), offset >= 0. 잘못된 값은 400.200 + {"items":[…]} · 400 잘못된 페이징
PATCH /v1/nodes/{id}부분 수정. body 에 title, body, meta 중 변경할 키만. Content-Type: application/json 필수.200 + 갱신된 node · 400 · 404 · 415
DELETE /v1/nodes/{id}soft delete. account 격리 위반은 404.204 · 400 · 404

한 흐름 예시

# 1) 노드 만들고 id 보관
NODE_ID=$(curl -fsS -X POST https://store.parklab.work/v1/nodes \
  -H "Authorization: Bearer $STORE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"kind":"Concept","title":"양자 얽힘"}' | jq -r .id)

# 2) 본문 채우기
curl -fsS -X PATCH "https://store.parklab.work/v1/nodes/$NODE_ID" \
  -H "Authorization: Bearer $STORE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"body":"두 입자가 측정 전까지 한 시스템처럼 행동하는 현상."}'

# 3) 다시 읽기
curl -fsS "https://store.parklab.work/v1/nodes/$NODE_ID" \
  -H "Authorization: Bearer $STORE_TOKEN"

# 4) 목록
curl -fsS "https://store.parklab.work/v1/nodes?limit=10" \
  -H "Authorization: Bearer $STORE_TOKEN"

# 5) 정리
curl -fsS -X DELETE "https://store.parklab.work/v1/nodes/$NODE_ID" \
  -H "Authorization: Bearer $STORE_TOKEN"

04account 격리

account A 의 토큰으로 account B 의 노드 id 에 접근하면 무조건 404 가 돌아옵니다 — 존재 자체를 알려주지 않습니다. 격리는 두 층에서 강제됩니다.

  1. application 레이어: 모든 SQL 에 WHERE account_id = $auth.account_id 가 들어갑니다.
  2. 그래프 레이어: account 마다 자체 AGE 그래프(acct_<uuid_hex>)가 있고, cypher 호출자는 graph 이름을 직접 넘길 수 없습니다.

새로 발급받은 토큰으로 처음 호출하면 GET /v1/nodes{"items":[]} 입니다 — 다른 사용자의 노드는 절대 새 시야에 들어오지 않습니다.

05관리자 엔드포인트

다음은 ADMIN_TOKEN 을 가진 관리자에게만 의미가 있습니다. /v1/admin/* 경로는 admin Bearer 가 필요합니다.

account 발급

curl -fsS -X POST https://store.parklab.work/v1/admin/accounts \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "alice"}'

응답(201)에는 idgraph_name(자동 derived: acct_<uuid_hex>) 이 들어 있습니다. 같은 이름이 이미 활성 상태면 409 Conflict 가 돌아옵니다.

토큰 발급

curl -fsS -X POST https://store.parklab.work/v1/admin/accounts/$ACCOUNT_ID/tokens \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "alice-laptop"}'

응답(201)의 token 필드가 사용자에게 한 번만 노출되는 plaintext 입니다. prefix (앞 12자) 는 안전하게 로그/감사에 적을 수 있는 식별자로 같이 돌아옵니다. 즉시 사용자에게 안전 채널로 전달.

토큰 회수 / account 삭제

curl -fsS -X DELETE https://store.parklab.work/v1/admin/tokens/$TOKEN_ID \
  -H "Authorization: Bearer $ADMIN_TOKEN"

curl -fsS -X DELETE https://store.parklab.work/v1/admin/accounts/$ACCOUNT_ID \
  -H "Authorization: Bearer $ADMIN_TOKEN"

둘 다 성공 시 204. 모르는 id 는 404. account 삭제는 그 account 의 노드와 AGE 그래프, 발급된 모든 토큰을 한 트랜잭션에서 함께 제거합니다 (cascade revoke).

관리자 응답 상태표

endpoint응답
POST /v1/admin/accounts201 · 400 잘못된 body · 409 이름 중복 · 415 Content-Type
DELETE /v1/admin/accounts/{id}204 · 400 잘못된 id · 404 없음
POST /v1/admin/accounts/{id}/tokens201 · 400 · 404 account 없음 · 415
DELETE /v1/admin/tokens/{id}204 · 400 · 404

모든 admin 엔드포인트는 Authorization: Bearer $ADMIN_TOKEN 미일치 시 401.

06헬스체크

endpoint의미인증
GET /healthz프로세스 살아 있음.없음
GET /readyzDB 핑 성공 시 200, 실패 시 503.없음

07현재 한계와 다음

다음 라운드 목표는 임베딩 자동 갱신 → 하이브리드 검색 → 엣지 CRUD 입니다. 그때까지는 노드 CRUD 와 격리 동작을 자유롭게 테스트해 주세요.