CLAWPOD ENGINEERING
에이전트 시크릿을 찾고, 위임하고, 지우는 법
Clawpod 메모리 시크릿의 검색·사용·회전·위임·삭제 흐름과 소유권, 세션, 감사 기록의 현재 보안 경계를 설명합니다.

API key, token, password를 일반 메모리(MEMORY.md), 작업 문서, 이슈에 적어 두면 검색과 협업 과정에서 값이 퍼질 수 있습니다. Memory Secrets는 보호 저장소의 값과 검색 가능한 설명을 분리하고, 값 대신 포인터를 선택한 뒤, 승인된 실행 순간에만 runtime이 해석하거나 주입하도록 돕습니다.
이 글은 현재 OpenClaw runtime의 도구 계약을 기준으로 한 실전 안내입니다. 도구 제공 범위와 비밀 입력 방식은 배포마다 다를 수 있으므로 운영자가 승인한 경로를 먼저 확인하세요.
1. Memory Secret과 catalog 이해하기
보호 저장소에는 credential 값이 들어가고, catalog와 검색 결과에는 값 없는 metadata가 나옵니다. 일반 메모리에는 포인터도 꼭 필요한 경우에만 기록하고 값은 기록하지 않습니다.
| 필드 | 용도 |
|---|---|
label |
사람이 구분하는 이름 |
service, host |
제품과 대상 위치 |
account |
credential이 속한 계정 |
kind, status |
token·password 같은 종류와 현재 상태 |
pointerId |
값을 복사하지 않고 선택하는 식별자 |
description, purpose |
운영 참고와 의도 설명 |
Metadata는 catalog, 검색, prompt 문맥에 보일 수 있습니다. 따라서 metadata에도 password, token, 복구 코드 같은 값을 넣지 마세요.
flowchart TD
I[승인된 비밀 입력 경로] --> S[보호된<br/>memory-secret store]
S --> M[값 없는 metadata<br/>catalog와 search]
M --> P[정확한 pointer 선택]
P --> R{누가 사용하나요?}
R -->|소유 세션| U[즉시 resolve 또는<br/>runtime injection]
R -->|하위 세션| G[세션에 묶인<br/>제한 위임]
G --> U
U --> T[대상 tool 또는 service]
T --> V[결과 확인 후<br/>위임 revoke]
linkStyle default fill:none,stroke:#64748b,stroke-width:1.5px
2. 일곱 도구의 역할
| 도구 | 언제 쓰나 |
|---|---|
memory_secret |
새 값을 보호 저장소에 저장하고 구조화 metadata를 등록할 때 |
memory_secret_search |
label뿐 아니라 service, host, account, kind로 기존 항목을 찾을 때 |
memory_secret_get |
소유 세션이 즉시 실행할 tool call에 값을 해석할 때 |
memory_secret_update |
기존 포인터의 값을 회전하거나 metadata를 고칠 때 |
memory_secret_dedupe |
저장소의 정확히 같은 값 중 정리 가능한 중복을 접을 때 |
memory_secret_delete |
소유 Agent의 포인터와 위임을 현재 store에서 영구 삭제할 때 |
memory_secret_delegate |
child session에 포인터 접근 grant를 만들거나 revoke할 때 |
Catalog와 search는 발견, get과 runtime injection은 사용입니다. 발견 결과에는 값을 싣지 않고, 해석한 값을 출력·일반 메모리·prompt·보고서로 다시 복사하지 않는 것이 운영 원칙입니다.
3. 안전하게 저장하고 먼저 검색하기
<available_memory_secrets>catalog에서 service, host, account를 확인합니다.- 바로 보이지 않으면
memory_secret_search로 label, service, host, account, kind를 검색합니다. - 그래도 없을 때만 새 credential이 필요한지 확인합니다.
- 자연어 대화에는 값이 아닌 metadata만 전달합니다.
- 실제 값은 배포 운영자가 제공한 승인된 보호 입력 경로에만 입력합니다. 일반 채팅, 문서, 이슈, 예제, 로그, shell history에는 넣지 않습니다.
- 저장 결과의 안전한 pointer metadata만 확인합니다.
안전한 요청 예시는 다음과 같습니다.
“
example-service의operator@example.com계정용 API token을 저장해 주세요. Host는api.example.test, kind는api-token, status는current입니다. 값은<ENTER_IN_APPROVED_PROTECTED_INPUT>경로로 제공하겠습니다. 대화에 값을 출력하지 마세요.”
문서와 테스트에는 TEST_ONLY_NOT_A_REAL_SECRET처럼 실제 credential로 오해하기 어려운 문자열만 사용하세요. 승인된 보호 입력 경로가 무엇인지 불분명하면 값을 받거나 저장하지 말고 운영자에게 확인해야 합니다.
4. 회전은 새 저장이 아니라 update입니다
값이 바뀌면 catalog에서 기존 포인터를 찾고 memory_secret_update로 회전합니다. Named field인 service, account, host, kind를 고치면 기존 metadata에 병합됩니다. 반면 raw metadata map을 전달하면 전체 map을 대체할 수 있어 빠진 항목이 사라질 수 있으므로 named field를 우선합니다.
“
example-service/operator@example.com포인터를 찾아 새 값으로 회전해 주세요. 새 값은<ENTER_IN_APPROVED_PROTECTED_INPUT>로 받으며 기존 pointerId를 유지하고, 안전한 metadata만 보고하세요.”
회전 전후 값은 서로 다른 값입니다. Dedupe가 회전된 두 값을 같은 credential로 임의 병합하지 않습니다.
5. 중복 제거는 핵심 수명주기 작업입니다
같은 session에서 같은 service/account로 동일한 값을 다시 저장하면 기존 포인터가 재사용될 수 있습니다. 과거에 쌓인 중복은 memory_secret_dedupe가 실제 값의 정확한 일치를 기준으로 정리합니다. 이름이 비슷하다는 이유만으로 합치지는 않습니다.
현재 보존 규칙은 다음과 같습니다.
- 같은 값 그룹에서는 최신 포인터를 유지합니다.
- 활성 delegation이 걸린 포인터는 진행 중인 child 작업을 끊지 않도록 유지합니다.
- 같은 값이어도 service 또는 account가 다르면 별도 credential로 유지합니다.
- 서로 다른 값과 회전된 값은 변경하지 않습니다.
Dedupe는 소유 Agent의 저장소에만 작용합니다. 실행 전 catalog/search 결과를 검토하고, 실행 뒤에는 유지·제거된 pointer ID만 확인하며 값을 보고서에 싣지 않습니다.
미리 보기: “중복 후보를 값 없이 service/account와 pointerId로만 정리해 보여 주세요. 아직 변경하지 마세요.”
실행: “보존 규칙을 적용해 내 memory-secret store의 중복을 정리하고, 유지·제거된 pointerId만 보고하세요.”
6. 삭제와 legacy tombstone 정리
memory_secret_delete는 owner Agent가 소유한 포인터와 그 위임을 현재 memory-secret store에서 물리적으로 제거합니다. 만든 세션이 끝났더라도 같은 owner Agent는 삭제할 수 있습니다. 이 작업은 현재 store에서 복구할 수 없으며, backup, snapshot, 입력 채널, 외부 서비스에 생긴 사본까지 지운다는 뜻은 아닙니다.
먼저 목록과 의존 작업을 확인하세요.
미리 보기: “
example-service의 폐기 후보를 pointerId와 metadata로만 보여 주세요. 삭제하지 마세요.”확인 후 실행: “확인한 pointer
<POINTER_ID_FROM_CATALOG>를 현재 store에서 hard delete하세요. 관련 delegation도 제거됨을 알리고 값은 출력하지 마세요.”
예전 soft-delete 방식으로 남은 tombstone은 store에 다음 write가 일어날 때 정리될 수 있습니다. 별도 파일 수정이나 임의 purge를 시도하지 마세요. 정상 포인터 삭제와 legacy tombstone cleanup은 구분해야 합니다.
7. Child Agent에는 값을 보내지 말고 grant를 위임하기
Owner에게 암호화된 msp_... 포인터는 다른 독립 Agent에게 보내도 그 Agent가 쓸 수 없고, 전달 자체도 피해야 합니다. 부모가 만든 실제 child session에는 다음 순서로 위임합니다.
- child를 먼저 만들고 반환된 정확한
childSessionKey를 확인합니다. pointerId + childSessionKey로 grant를 만듭니다.- 기본 grant는 child session 수명 동안 유효합니다. 더 짧게 제한하려면
ttlSeconds, 한 run에 묶어야 할 때만 고급 옵션childRunId를 사용합니다. - Child는 허용된 session에서
memory_secret_get또는 runtime injection으로 직접 사용합니다. 부모가 값을 메시지에 넣지 않습니다. - 결과를 확인하면
revokeDelegationId로 즉시 revoke합니다. Secret 자체를 삭제하면 연결된 위임도 제거됩니다.
purpose와 allowedTargets는 문서용 메모이며 현재 접근 제어로 강제되지 않습니다. “배포 전용”이라고 적는 것만으로 다른 대상 사용이 차단되지는 않습니다. 실제 경계는 child session/run binding, TTL, revoke와 대상 tool 권한입니다.
위임: “
<POINTER_ID_FROM_CATALOG>를 방금 만든 child session<EXACT_CHILD_SESSION_KEY>에 15분만 위임하세요. 값을 메시지로 보내지 말고 delegation ID만 보고하세요.”회수: “작업 결과를 확인했습니다. delegation
<DELEGATION_ID>를 revoke하고 상태만 보고하세요.”
8. 이제 어떻게 하면 잘 쓰나
Memory Secrets를 잘 쓰려면 두 관점을 함께 지켜야 합니다. **운영자(사용자)**는 Agent가 같은 credential을 다시 찾고 안전하게 갱신할 수 있도록 값이 아닌 문맥을 충분히 제공해야 합니다. Agent는 catalog와 metadata를 먼저 확인하고, 실제 값은 승인된 보호 경로와 필요한 runtime 경계 안에서만 다뤄야 합니다.
운영자 관점: 값을 설명하지 말고 credential의 문맥을 설명하기
service,account,host,kind를 가능한 한 구체적으로 알려 주세요. 같은 서비스에 계정이 여러 개거나 같은 계정이 여러 host에 쓰일 때 특히 중요합니다.- “새로 저장해 줘”보다 “기존 항목을 catalog와 metadata에서 먼저 검색해 줘”라고 요청하세요.
- 값이 바뀌면 “다시 저장”이 아니라 “찾은 pointer를 update/rotate해 줘”라고 요청하세요. 그래야 기존 참조와 수명주기가 이어집니다.
- Dedupe 전에는 합쳐질 후보와 보존될 항목을, delete 전에는 정확한 pointer와 active delegation을 먼저 보여 달라고 요청하세요.
- Hard delete는 현재 보호 store의 해당 owner-Agent record와 active delegation을 되돌릴 수 없게 제거합니다. 백업이나 외부 사본까지 지운다는 뜻은 아닙니다. 범위를 확인하고 명시적으로 승인하세요.
- 실제 credential 값은 배포 운영자가 승인한 보호 입력 또는 secret-storage 경로로만 입력하세요. Ordinary chat, 문서, issue, 로그, source code에는 넣지 마세요.
Agent에게 보낼 일반 메시지에는 값 대신 다음처럼 label이 붙은 문맥 블록을 사용하면 좋습니다.
[Credential metadata — documentation-only]
Title: <HUMAN_READABLE_LABEL>
URL: https://service.example.com/path
Host: service.example.com
Email: operator@example.com
Account: <ACCOUNT_OR_USERNAME>
ID: <OPTIONAL_NON_SECRET_ID>
kind: <password|token|api-key|oauth|email|credential>
service: <SERVICE_NAME>
description: <NON_SECRET_PURPOSE_AND_SCOPE>
value: <ENTER_ONLY_THROUGH_APPROVED_PROTECTED_INPUT>
Request: Search the catalog and metadata first. If this credential exists, update/rotate that pointer instead of storing another copy.
value 행은 실제 값을 붙여 넣는 자리가 아니라 보호 입력이 별도로 필요함을 표시하는 label입니다. 실제 값은 이 블록과 분리된 승인 경로로 전달해야 합니다.
문맥 블록을 structured fields로 옮기는 방법
| 블록 항목 | Structured field | 작성 기준 |
|---|---|---|
Title |
label |
사람이 목록에서 구분할 수 있는 짧은 이름. 값이나 token 일부를 넣지 않습니다. |
URL |
주로 host, 필요 시 description |
URL에서 hostname만 host로 정규화하고, non-secret path 문맥이 꼭 필요할 때만 description에 적습니다. Query나 fragment에는 credential을 넣지 않습니다. |
Host |
host |
서버 IP 또는 hostname. 문서 예시는 reserved IP나 example.com 하위 도메인을 사용합니다. |
Email |
account |
서비스 로그인 identity가 email일 때 사용합니다. |
Account |
account |
Username, bot handle, tenant 계정처럼 서비스를 사용하는 identity입니다. |
ID |
account 또는 description |
로그인 identity면 account, 단순 non-secret resource ID면 description에 역할과 함께 기록합니다. |
kind |
kind |
password, token, api-key, oauth, email, credential처럼 credential 유형을 일관되게 씁니다. |
service |
service |
Atlassian, SSH, Claude Code, LiteLLM처럼 시스템 또는 vendor를 식별합니다. |
value |
보호 store의 secret value | Ordinary chat이나 metadata에 복사하지 않고 승인된 보호 입력/secret-storage 경로로만 저장합니다. |
description |
description 또는 purpose |
비밀값 없이 용도와 범위를 설명합니다. Runtime delegation의 purpose와 혼동하지 마세요. |
문서용 입력 예시
아래 값은 모두 문서 전용 placeholder입니다. 실제 credential처럼 사용하거나 일반 메시지의 placeholder를 실제 값으로 바꾸지 마세요.
Atlassian API token
[Credential metadata — documentation-only]
Title: Atlassian API token for documentation workspace
URL: https://example.atlassian.net
Host: example.atlassian.net
Email: docs-operator@example.com
Account: docs-operator@example.com
kind: api-key
service: atlassian
description: Documentation workspace automation
value: <ENTER_ONLY_THROUGH_APPROVED_PROTECTED_INPUT>
SSH server password
[Credential metadata — documentation-only]
Title: Example staging SSH credential
Host: 192.0.2.44
Account: deploy-example
kind: password
service: ssh
description: Documentation-only staging host in TEST-NET-1
value: <ENTER_ONLY_THROUGH_APPROVED_PROTECTED_INPUT>
Claude setup token
[Credential metadata — documentation-only]
Title: Claude Code setup token for example operator
Host: console.example.com
Account: claude-operator@example.com
kind: oauth
service: Claude Code
description: Documentation-only CLI onboarding
value: <ENTER_ONLY_THROUGH_APPROVED_PROTECTED_INPUT>
LiteLLM host SSH key or password
[Credential metadata — documentation-only]
Title: LiteLLM example host SSH credential
URL: https://litellm.example.com
Host: 198.51.100.27
Account: litellm-admin-example
kind: credential
service: litellm-ssh
description: Documentation-only administration of the example LiteLLM host
value: <ENTER_ONLY_THROUGH_APPROVED_PROTECTED_INPUT>
Portal login
[Credential metadata — documentation-only]
Title: Example customer portal login
URL: https://portal.example.com/login
Host: portal.example.com
Email: portal-user@example.com
Account: portal-user@example.com
ID: customer-example-001
kind: password
service: example-portal
description: Documentation-only portal login; ID is a non-secret resource identifier
value: <ENTER_ONLY_THROUGH_APPROVED_PROTECTED_INPUT>
Agent 관점: catalog에서 시작해 수명주기를 닫기
- Catalog first: 현재 제공된 safe catalog metadata에서
service,account,host,kind가 맞는 후보를 먼저 찾습니다. - Search before declaring missing: catalog에 명확한 항목이 없으면 같은 metadata로
memory_secret_search를 실행합니다. 검색이 비었을 때만 새 credential이 필요한지 운영자에게 확인합니다. - Save with structure: 새 값을 저장할 때
label,service,account,host,kind,status,purpose를 일관되게 채웁니다. Metadata에는 실제 값이나 값의 일부를 넣지 않습니다. - Rotate by update: 값 또는 문맥이 바뀌면
memory_secret_update로 owner Agent의 기존 pointer를 회전합니다. Rawmetadatamap은 전체 교체이므로 named fields를 우선합니다. - Dedupe without crossing identity boundaries: Exact same value의 중복만 다루고, 가장 새 pointer를 유지하되 active delegation이 있는 pointer는 보존합니다. 같은 값이어도
service/account가 다르면 별도 credential로 유지하며, distinct 또는 rotated value는 건드리지 않습니다. - Delete only in the scoped store: 삭제 전 pointer를 재확인합니다. Hard delete는 현재 보호 store에 있는 현재 owner Agent의 record와 그 active delegations을 영구 제거합니다. 백업, 외부 시스템, 문서나 로그에 이미 생긴 사본까지 삭제한다고 주장하지 않습니다.
- Resolve only for immediate use: Runtime tool call 직전에 필요한 pointer를 resolve하거나 runtime injection을 사용합니다. Resolve된 값은 응답, ordinary prompt/chat, 로그, 일반 memory, 보고서, 파일, issue, source code, 검색 index에 출력하거나 옮기지 않습니다.
- Delegate narrowly and revoke: 실제 child session의 정확한
childSessionKey에 grant하고, 필요하면 짧은ttlSeconds또는 고급childRunIdbinding을 사용합니다.purpose와allowedTargets는 advisory 문서 필드일 뿐 강제 경계가 아닙니다. 결과를 확인한 뒤revokeDelegationId로 revoke하며, owner-encrypted pointer나 값을 독립 Agent에게 보내지 않습니다.
9. 자주 묻는 질문
Q. Catalog나 검색에 실제 값이 나오나요?
아니요. 도구 계약상 안전한 metadata와 pointer ID를 발견하는 단계입니다. 다만 metadata는 보일 수 있으므로 그 필드에도 비밀값을 넣지 않아야 합니다.
Q. 값을 어디에 입력해야 하나요?
배포 운영자가 승인한 보호 입력 경로를 사용합니다. 그런 경로가 확인되지 않았다면 ordinary chat, 문서, issue, shell history에 값을 붙여 넣지 말고 먼저 확인하세요.
Q. 같은 password를 여러 service에서 쓰면 dedupe가 합치나요?
Service/account가 다르면 별도 credential로 보존합니다. 활성 delegation이 있는 포인터도 보존하며, 서로 다른 값이나 회전된 값은 건드리지 않습니다.
Q. Hard delete 뒤 복구할 수 있나요? Tombstone은 언제 없어지나요?
현재 store에서는 복구할 수 없습니다. Legacy tombstone은 이후 store write에서 정리될 수 있지만, store 밖의 사본은 별도 정책으로 처리해야 합니다.
Q. 위임은 자동으로 안전한 target에만 제한되나요?
purpose와 allowedTargets는 강제 정책이 아닙니다. 정확한 child session, 필요 시 run, 짧은 TTL, 명시적 revoke와 target tool 권한을 함께 사용해야 합니다.
Memory Secrets는 모든 환경에서 평문이 사라진다고 보장하는 기능이 아닙니다. 값 없는 metadata로 발견하고, 승인된 보호 입력과 제한된 runtime 경로로 사용하며, 회전·dedupe·revoke·delete로 수명주기를 닫는 운영 방식입니다.

