
AI 에이전트를 위한 자격 증명 격리. 에이전트는 실제 API 키를 절대 보지 않습니다 - 구조적 보장이지 정책이 아닙니다.
AI 에이전트를 위한 자격 증명 방화벽.
핵심 주장은 구조적이며 정책이 아닙니다. 에이전트는 실제 API 키가 아닌 플레이스홀더 토큰을 받습니다. 실제 키는 단 하나의 네트워크 경계, 즉 wardn 프록시 내부에서 업스트림 API로 가는 도중에만 교차하며, 응답이 에이전트에 도달하기 전에 응답에서 제거됩니다. 로그, 환경, LLM 컨텍스트 창, 임시 파일, 쉘 히스토리에는 플레이스홀더만 보관됩니다.```text agent process OPENAI_KEY=wdn_placeholder_a1b2c3d4e5f6g7h8 (useless) agent logs Authorization: Bearer wdn_placeholder_a1b2... (useless) LLM context wdn_placeholder_a1b2c3d4e5f6g7h8 (useless) wardn proxy injects the real key in-flight, single seam (deleted on response) ~/.vibeguard/vault.enc AES-256-GCM(Argon2id(passphrase)) (encrypted at rest)
이것은 핵심 주장이며 오늘날 에이전트 손상, 프롬프트 인젝션, 로그 도난, 스킬 유출에 대해 방어 가능합니다. 어떤 것이 보호되고 어떤 것이 보호되지 않는지에 대한 솔직한 구분 — 더 강력한 '호스트 손상 시 아무것도 유출되지 않음' 주장이 도달 가능한 계층을 포함하여 — 에 대해서는 [docs/THREAT-MODEL.md](https://github.com/rohansx/wardn/blob/main/docs/THREAT-MODEL.md)를 읽어보세요.
볼트 자체(저장 시 암호화, 암호문 파생 키)는 실제 구성 요소이며 방화벽이 단일 시스템에서 실행될 수 있는 이유입니다. 곧 제공될 [docs/HOSTED-TIER.md](https://github.com/rohansx/wardn/blob/main/docs/HOSTED-TIER.md) 계층은 추가로 프록시를 기밀 컴퓨팅 엔클레이브로 감싸 완전히 손상된 VPS조차 키를 읽을 수 없도록 합니다.
[](https://crates.io/crates/wardn)
[](LICENSE)
## 문제
오늘날 모든 AI 에이전트 프레임워크는 API 키를 환경 변수나 `.env` 파일에 저장합니다. 손상된 에이전트, 악성 스킬, 일반적인 스틸러, 또는 LLM 로그에서 `Authorization: Bearer sk-...`를 유출하는 프롬프트 인젝션은 여러분의 자격 증명에 완전히 접근할 수 있습니다.```
~/.env → OPENAI_KEY=sk-proj-real-key # plaintext, readable by anyone
agent context → "Use OPENAI_KEY=sk-proj-real-key" # leaked into LLM context window
agent logs → Authorization: Bearer sk-proj-... # sitting in log files
wardn은 에이전트에게 쓸모없는 플레이스홀더 문자열을 건네주고, 도달할 수 있는 모든 표면에서 실제 키를 제거합니다. 실제 키는 네트워크 계층 — 단일 이음매 — 에서 주입되며, 에이전트에 도달하기 전에 응답에서 제거됩니다.``` agent environment → OPENAI_KEY=wdn_placeholder_a1b2c3d4e5f6g7h8 (useless) wardn vault → OPENAI_KEY=sk-proj-real-key (encrypted at rest) upstream request → Authorization: Bearer sk-proj-real-key (network transit only) upstream response → ...real keys stripped, placeholders returned... (re-injected on the way back) agent logs → Authorization: Bearer wdn_placeholder_a1b2... (useless) LLM context window → wdn_placeholder_a1b2c3d4e5f6g7h8 (useless)
## 아키텍처```mermaid
flowchart TB
subgraph Agent["AI Agent Process"]
A1["Agent Code"]
A2["ENV: OPENAI_KEY=wdn_placeholder_a1b2..."]
end
subgraph Wardn["wardn daemon · localhost:7777"]
direction TB
P["HTTP Proxy"]
MCP["MCP Server\n(stdio)"]
subgraph Pipeline["Request Pipeline"]
direction LR
S1["Identify\nAgent"] --> S2["Resolve\nPlaceholder"] --> S3["Check\nAuth"] --> S4["Rate\nLimit"] --> S5["Inject\nReal Key"]
end
subgraph ResponsePipeline["Response Pipeline"]
direction RL
R1["Strip Real\nKeys"] --> R2["Replace with\nPlaceholders"]
end
subgraph Vault["Encrypted Vault"]
V1["AES-256-GCM"]
V2["Argon2id KDF"]
V3["Placeholder Map\nper agent × credential"]
end
end
subgraph External["External APIs"]
E1["api.openai.com"]
E2["api.anthropic.com"]
E3["..."]
end
A1 -- "placeholder token\nin headers/body" --> P
A1 -. "MCP: get_credential_ref\nlist_credentials\ncheck_rate_limit" .-> MCP
MCP -. "placeholder token\n(never real keys)" .-> A1
P --> Pipeline
Pipeline --> External
External --> ResponsePipeline
ResponsePipeline -- "response with\nplaceholders only" --> A1
Pipeline <--> Vault
ResponsePipeline <--> Vault
style Agent fill:#1a1a2e,stroke:#e94560,color:#fff
style Wardn fill:#0f3460,stroke:#16213e,color:#fff
style Pipeline fill:#16213e,stroke:#e94560,color:#fff
style ResponsePipeline fill:#16213e,stroke:#e94560,color:#fff
style Vault fill:#1a1a2e,stroke:#00d2ff,color:#fff
style External fill:#0a0a0a,stroke:#533483,color:#fff
Agent sends request with placeholder in Authorization header │ ▼ ┌─────────────────────────┐ │ wardn proxy │ │ localhost:7777 │ │ │ │ 1. Identify agent │ │ 2. Resolve placeholder │ │ 3. Check authorization │ │ 4. Check rate limit │ │ 5. Inject real key │ │ 6. Forward request │ │ 7. Strip key from resp │ │ 8. Return to agent │ └─────────────────────────┘ │ ▼ External API (only place real key exists in transit)
## 데모
<p align="center">
<img src="https://assets.kitploit.com/production/public/readmes/12823/1fa6109ffd855ec98c173c5edd2d7ee77f6b0c918a3cdecb1ea5fbfe8326161d.gif" alt="wardn 데모" width="800">
</p>
## 신뢰 수준, 정직하게
| 계층 | 위치 | 보유 내용 |
|---|---|---|
| **자가 호스팅 (현재)** | 당신의 노트북, VPS, CI | 저장 시 암호화된 볼트, 에이전트에 대한 방화벽 주장. **호스트의 루트에 대해서는 방어하지 않습니다.** |
| **호스팅 (예정)** | wardn 관리 또는 BYO 클라우드 | 기밀 컴퓨팅 엔클레이브 (Nitro / SEV-SNP) + 원격 증명 + 프록시로 암호화 흐름. 실제 "호스트 손상 시 아무것도 유출되지 않음" 주장. |
자가 호스팅 계층은 핵심 주장이며 현재 제공됩니다. 호스팅 계층은 엄격한 업그레이드 경로로서 비용과 운영 복잡성이 수반되며, 그 설계는 [docs/HOSTED-TIER.md](https://github.com/rohansx/wardn/blob/main/docs/HOSTED-TIER.md)에 있습니다. 적용되는 것과 적용되지 않는 것의 완전하고 정직한 목록:
👉 **[docs/THREAT-MODEL.md](https://github.com/rohansx/wardn/blob/main/docs/THREAT-MODEL.md)** — 적용/미적용 표, "소프트웨어 볼트로 호스트 손상을 제거할 수 없다"는 점이 명확히 언급되어 있으며, 업그레이드 경로가 포함되어 있습니다.
## 설치```bash
# Prebuilt binary (Linux/macOS, amd64/arm64), checksum-verified
curl -sSf https://raw.githubusercontent.com/rohansx/wardn/main/install.sh | sh
# or from crates.io
cargo install wardn
# or Homebrew, once the tap is published (see Formula/wardn.rb)
brew install rohansx/wardn/wardn
wardn vault create wardn vault set OPENAI_KEY wardn vault set ANTHROPIC_KEY
wardn setup claude-code
그렇습니다. 이제 Claude Code는 환경에서 실제 키를 읽는 대신 wardn의 MCP 서버를 사용하여 플레이스홀더 토큰을 가져옵니다.
### 다음에 발생하는 일
1. Claude Code가 `get_credential_ref`를 호출 → `wdn_placeholder_a1b2...`를 가져옵니다 (실제 키가 아님)
2. 에이전트가 플레이스홀더와 함께 wardn 프록시를 통해 요청을 보냅니다
3. 프록시가 플레이스홀더를 실제 키로 교체하여 API로 전달합니다
4. 프록시가 응답에서 실제 키를 제거한 후 에이전트에 반환합니다
실제 키는 에이전트의 메모리, 로그 또는 LLM 컨텍스트 창에 절대 들어가지 않습니다.
## 로컬 대시보드
데몬이 실행되면 (`wardn serve` 또는 `wardn run`에 의해 생성됨), 브라우저에서
**http://127.0.0.1:7777/ui**를 엽니다. 읽기 전용, 로컬 전용 보기:
- **자격 증명** — 모든 저장된 자격 증명과 해당 ACL(허용된 에이전트,
허용된 도메인, 속도 제한 + 예산 배지)
- **최근 활동** — method, domain, path, status, agent, request_id 및 기록된 비용(`request_completed`,
`credential_injected`, `rate_limit`, `budget_exceeded`, `loop_detected`,
`request_error`)을 포함한 최근 50개의 프록시 이벤트
- **예산** — 각 자격 증명의 구성된 예산(max, spent,
remaining, window, mode)과 50%/80%를 넘으면 경고 → 나쁨으로 변하는 진행 표시줄
2초마다 자동으로 폴링됩니다. 변경 엔드포인트는 없습니다 — 대시보드에서 나가는 유일한
방법은 API 자체(`/api/summary`, `/api/credentials`,
`/api/audit?limit=N`, `/api/budgets`)입니다.```bash
# Static, anonymous, never sees real keys
curl http://127.0.0.1:7777/api/summary | jq
wardn vault get OPENAI_KEY
wardn vault list
wardn serve
wardn serve --mcp --agent my-agent
## CLI 참조