跳转到内容

安全

Adopted principles: architecture.md. Security review and implementation items: GitHub Issue #33, #46.

逸灵风设计为单人本地 / 内网部署:

  • Habitat RPC REST (/rpc/v1/* except health probe/CORS/echo) requires a Service API Token (Authorization: Bearer fa_at_…); create with anima token create. Binding 127.0.0.1 limits network exposure but does not replace token auth — any local process that can reach the port still needs a valid token for business routes.
  • 默认 bind 为 127.0.0.1;若需局域网访问,请自行评估 CORS 与网络隔离。
  • Do not expose the service to the public internet without TLS and token-protected clients (see remote-access.md)。可选 http.tls.mode: acme(Let’s Encrypt HTTP-01,公网 :80 challenge + :2659 HTTPS)仍须 Token;不替代防火墙 / 反向代理 harden。
RuleDescription
Sole authoritative storeVault (ECS vault_item in User + Agent libraries); legacy pass (~/.password-store) is read-only on disk after migration — runtime no longer uses pass CLI
Versioned updatesSubstantive vault item updates keep up to 10 prior snapshots (entities.revisions); restore via Shell /vault history. See entity-revisions
Never commit secretsDo not write API keys, tokens, DB passwords into git. Bootstrap config.yaml: use env() (or keep secrets out of the file). Runtime LLM/MCP settings live in PG — use vault() / env() there
Runtime directory~/.anima/ (FREEANIMA_HOME overridable) holds config, agent machine key (vault/agent-machine.key), conversations, memory—recommend chmod 700
User master passwordSet only in Shell /vault、bundled Chat unlock,或 Vault 浏览器扩展选项解锁;never sent as a chat message or stored in PG messages
Chat User vault unlockv1 bundled Chat onlysrc/portal/app/web / desktop / mobile);Discord / WeChat gateways cannot unlock User library
Browser extension浏览器形态入口(src/portal/extension);Service API Token 存 chrome.storage.local;主密码仅扩展内存(默认 15min);RPC 走 background host_permissions。见 portal.mdvault.md

config.yaml is bootstrap only (read before PostgreSQL is up). Secrets there support plaintext or env("KEY")not vault() (Vault items live in PG). After Habitat is connected, runtime config in PG may use vault("item_id", "field") and env("KEY"). Agent tools that need CLI credentials pass per-call secrets[] on terminal_run / code_execute (child env only). Browser form fields use browser_type secret (typed into the page; never echoed in tool results). User-library resolution still requires an unlocked Chat session on the client.

SurfaceUser libraryAgent library
Habitat PGCiphertext + verifier onlyCiphertext + machine key file on disk
LLMMetadata only; subprocess secrets[] / browser_type secretMetadata only; subprocess secrets[] / browser_type secret
ShellClient master key in memoryHabitat decrypt over loopback SAP
路径内容加密
PostgreSQL 对话存档conversations / messages无应用层加密
~/.anima/vault/agent-machine.keyAgent 保险库机读密钥文件权限(chmod 600
~/.anima/weixin/微信同步状态

磁盘备份 = 数据访问。请妥善保护备份介质。

CapabilityRisk
terminal_runDefault shell: false (argv spawn). Explicit shell: true enables pipes/redirection. Optional secrets[] decrypts Vault into that subprocess env only (not Habitat process.env). Always-on hard deny for catastrophic targets—not an OS sandbox; bypass via code_execute / interpreters remains
file_read / file_write / file_delete / file_searchPath deny: /etc, /proc, /sys, dangerous /dev, ~/.ssh private keys, FREEANIMA_HOME/vault. Heuristic deny ≠ writable-root sandbox
code_executeNo shell (Bun/Node argv). Optional secrets[] same as terminal_run. Arbitrary JS can still use node:fs—not a container sandbox
browser_typeOptional secret decrypts one Vault field and types it into the page; tool result redacts typed as *** (never plaintext)
MCP toolsCapabilities entirely determined by external Server; stdio default, SSE auth scheme not fully defined
Capability mask (Mask)Conversation-level tool whitelist; deny overrides allow; LLM cannot see policy details; see src/host/core/mask/
ACP (Cursor)Default auto-approve all session/request_permission (allow-once)
vault_list / vault_search / vault_get_metaVault metadata only; no secret values. Habitat-only (not MCP)
vault_create / vault_update / vault_deleteHabitat-only (not MCP). create/update seal plaintext into Agent library only; tool results are metadata only. User library writes stay in Vault UI
  1. Discovervault_list / vault_search / vault_get_meta (metadata only; never plaintext in tool results).
  2. Write (Agent library)vault_create / vault_update / vault_delete in Habitat chat only. create/update accept plaintext secrets to seal on Habitat; results return metadata only. User library: Vault UI. (Entire vault ToolSet is not MCP-exposed.)
  3. Use — pass secrets: [{ id, env_name, field?, subject_kind? }] on the same terminal_run or code_execute call that needs the credential (e.g. GH_TOKEN for gh); or secret: { id, field } on browser_type for form fields. For field: "totp", the resolved value is the current TOTP code (RFC 6238), not the stored Base32 secret.
  4. Scope — plaintext is decrypted for that call only (secrets[] → child env; browser_type secret → Camofox type payload). It is not written to Habitat process.env and not returned in tool results. Default shell=false: use argv form (printenv GH_TOKEN, gh …), not echo $VAR.
MeasureDescription
Same-origin RPCTanStack Start server functions same-origin by default, no CORS whitelist needed
Config API secretsHabitat config GET returns secrets in cleartext (api_key, MCP env / headers, etc.). Legacy "***" on write-back is restored via restoreMaskedSecrets
MCP configsanitizeConfigForApi keeps MCP env / headers plaintext so Habitat MCP page can edit round-trip
File path policyShared path-policy for file_* tools: /etc, vault, ssh private keys, /proc//sys, blocked devices
Terminal shell default offterminal_run default shell=false; pass shell=true only when pipes/redirection are needed (friction, not a sandbox)
Terminal command hard denyAlways-on catastrophic command policy (terminal-command-policy); cannot be disabled via env
Slash commandsWhitelist routing; every command must produce user-visible feedback; long-running commands send an immediate ack then the final result
MCP default stdioReduces port exposure
Vault isolationLLM sees vault item metadata only, not decrypted fields
Service API TokenHabitat RPC REST /rpc/v1/* routes require Authorization: Bearer fa_at_… (service_api_tokens PG table); health probe/CORS/echo exempt.
CI secret scanning.github/workflows/security.yml (Gitleaks); GitHub Secret scanning + Push protection (free for public repos)
.gitignore.env.*, config.yaml, private key suffixes

已知缺口(文档 ≠ 已完全实现)

Section titled “已知缺口(文档 ≠ 已完全实现)”

以下在代码或文档中规划,部署者请勿假设已实现

PriorityItemStatus
P0file_* path deny (/etc/, vault, ssh, …)Implemented (path-policy)
P0terminal_run default shell=falseImplemented
P0Terminal catastrophic command hard denyImplemented (heuristic; ≠ sandbox)
P0code_execute no shellImplemented (JS FS still open)
P1Runtime Unix socket chmod 600 + handshake tokenNot implemented
P1FREEANIMA_WRITE_SAFE_ROOT / READ_SAFE_ROOTNot implemented
P2(retired) Config API field redaction maintenanceDropped — secrets not masked (incl. MCP env/headers)
P3IPC / LLM rate limitingNone
P3Session disk encryptionNone
代号名称说明
A外网攻击未授权访问、端口暴露
BLLM 层注入Prompt 注入、工具参数操纵、命令注入
CAgent 过失误执行危险操作
D依赖链三方库 / MCP / ACP 被攻陷
E数据安全对话泄露、密钥泄露、记忆篡改
ModuleA ExternalB LLM injectionC Agent errorD DependenciesE Data
RuntimeDefault 127.0.0.1 bindMaxTurnsExceededGap: rate limitingllm client vulnsPG unencrypted
GatewayToken in Vault / envMalicious messagesReply with sensitive infoSDK vulns
CLI / ToolsLocal shell compromisedpath deny + terminal hard deny (bypass via code_execute possible)Reduced catastrophic rmLogs may contain conversations
HTTP / Habitatservice_api_tokens Bearer token(所有来源,含 loopback)BFF does not touch LLM params directlyconfig displayVue/axiosSSE plaintext
MCP / ACPSSE auth undefinedMalicious paramsWrong delegationServer compromisedContext may contain sensitive data
VaultAgent machine key file permissionsMetadata-only tools; per-call secrets[] / browser_type secretWrong item / env_nameWeb CryptoUser MP never in PG messages
  • Read/write/delete/search deny: /etc/, /proc/, ~/.ssh/ private keys, vault under FREEANIMA_HOME
  • Optional FREEANIMA_READ_SAFE_ROOT still P1

P0 — Shell execution + command hard deny (landed)

Section titled “P0 — Shell execution + command hard deny (landed)”
  • terminal_run default shell=false; pass shell=true only for pipes/redirection (not a sandbox)
  • Always-on deny for catastrophic rm/rmdir, mkfs*, dd of=/dev/…, fork bombs, power commands, recursive chmod/chown on / or $HOME, destructive find on system/home roots
  • code_execute remains argv-only (no shell); not a process sandbox
  • Unix socket chmod 600 + 可选握手 token
  • 写安全根默认 cwd(FREEANIMA_WRITE_SAFE_ROOT
  • When adding secret fields, sync config sanitization in platform
  1. Copy config.example.yaml~/.anima/config.yaml; use env() for bootstrap secrets — do not write plaintext secrets in config; vault() is for PG runtime config after Habitat is up
  2. Open Shell /vault; set User master password; migrate secrets from legacy pass if needed
  3. chmod 700 ~/.anima (includes vault/agent-machine.key)
  4. 仅 bind 127.0.0.1,或确保内网隔离
  5. Review inbound MCP / ACP in Habitat UI (/habitat/mcp, ACP page); set enabled: false for untrusted external Servers
  6. 定期备份 ~/.anima/(若保留则含遗留 ~/.password-store);备份介质加密存储
  7. 勿将 .envconfig.yaml 提交到 git
  8. Do not pass shell=true on terminal_run unless you intentionally need pipes/redirection