:PROPERTIES: :ID: credentials-vault-skill :CREATED: [2026-04-09 Thu] :END: #+TITLE: SKILL: Credentials Vault (Universal Literate Note) #+STARTUP: content #+FILETAGS: :auth:security:infrastructure:psf: #+DEPENDS_ON: id:state-persistence-skill * Overview The *Credentials Vault* is the high-security enclave for the Org-Agent. It centralizes the management of LLM API keys, OAuth sessions, and browser cookies. By consolidating these into a single vault, we ensure that sensitive tokens are handled with uniform masking, validation, and Merkle-integrated persistence. * Phase A: Demand (PRD) :PROPERTIES: :STATUS: SIGNED :END: ** 1. Purpose Securely manage all authentication tokens required for the PSF to operate. ** 2. User Needs - *Unified Storage:* Single interface for API keys and Session Cookies. - *Masked Logging:* Ensure credentials never appear in plaintext in `harness-log`. - *Guided Onboarding:* Retain and improve the Google/Gemini cookie handshake. - *Persistence:* Securely save credentials to the Memory via Merkle-Tree snapshots. * Phase B: Blueprint (PROTOCOL) :PROPERTIES: :STATUS: SIGNED :END: ** 1. Architectural Intent The vault provides a secure lookup table in RAM, backed by the persistent Memory. Access is restricted to internal kernel requests and explicitly authorized deterministic gates. ** 2. Semantic Interfaces #+begin_src lisp (defun vault-get-secret (provider &key type) "Retrieves a secret (api-key or session) for a provider.") (defun vault-set-secret (provider secret &key type) "Securely stores a secret and triggers a Merkle snapshot.") #+end_src * Phase C: Success (QUALITY) :PROPERTIES: :STATUS: SIGNED :END: ** 1. Success Criteria - [ ] *No Plaintext Leaks:* Log output must use `[REDACTED]` for sensitive values. - [ ] *Merkle Integration:* Setting a secret must increment the Memory version. - [ ] *Dual-Path Auth:* Support both `:api-key` and `:session-cookies`. - [ ] *Onboarding Verification:* The cookie handshake successfully hydrates the vault. ** 2. TDD Plan Tests in `tests/vault-tests.lisp` will verify: 1. Retrieval of keys from both `.env` (fallback) and Vault (primary). 2. Redaction of keys in log strings. 3. Successful version increment in the Memory after `vault-set-secret`. * Phase D: Build (Implementation) ** Package Context #+begin_src lisp #+end_src ** Vault State We maintain an in-memory hash table for secrets, which is hydrated from and persisted to the Memory. #+begin_src lisp (defvar *vault-memory* (make-hash-table :test 'equal) "In-memory cache of sensitive credentials.") #+end_src ** Helper: Secret Masking The `vault-mask-string` function ensures that diagnostic output never contains the full plaintext of a sensitive token. #+begin_src lisp (defun vault-mask-string (str) "Returns a masked version of a sensitive string." (if (and str (> (length str) 8)) (format nil "~a...~a" (subseq str 0 4) (subseq str (- (length str) 4))) "[REDACTED]")) #+end_src ** Retrieval (vault-get-secret) This function is the secure getter for all system secrets. It prioritizes the Vault (Memory) and falls back to environment variables for legacy compatibility. #+begin_src lisp (defun vault-get-secret (provider &key (type :api-key)) "Retrieves a credential. Type can be :api-key or :session." (let* ((key (format nil "~a-~a" provider type)) (val (gethash key *vault-memory*))) (if val val ;; Fallback to environment (let ((env-var (case provider ((:gemini :gemini-api) "GEMINI_API_KEY") (:openai "OPENAI_API_KEY") (:anthropic "ANTHROPIC_API_KEY") (:groq "GROQ_API_KEY") (:openrouter "OPENROUTER_API_KEY") (:telegram "TELEGRAM_BOT_TOKEN") (:signal "SIGNAL_ACCOUNT_NUMBER") (:matrix-homeserver "MATRIX_HOMESERVER") (:matrix-token "MATRIX_ACCESS_TOKEN") (t nil)))) (when (and env-var (eq type :api-key)) (uiop:getenv env-var)))))) #+end_src ** Persistence (vault-set-secret) When a secret is updated, we immediately snapshot the Memory to ensure the credential change is versioned and durable. #+begin_src lisp (defun vault-set-secret (provider secret &key (type :api-key)) "Securely stores a secret and triggers a Merkle snapshot." (let ((key (format nil "~a-~a" provider type))) (setf (gethash key *vault-memory*) secret) (harness-log "VAULT - Updated ~a for ~a. Triggering Merkle snapshot..." type provider) (snapshot-memory) t)) #+end_src ** Onboarding Logic Retained from the legacy Google skill, this provides the instructions for the sovereign cookie handshake. #+begin_src lisp (defun vault-onboard-gemini-web () "Instructions for the Sovereign Cookie Handshake." (harness-log "--- GEMINI WEB ONBOARDING ---") (harness-log "1. Visit gemini.google.com") (harness-log "2. Run the 'Get Gemini Cookies' Bookmarklet.") (harness-log " CODE: javascript:(function(){const c=document.cookie.split('; ').reduce((r,v)=>{const [n,val]=v.split('=');r[n]=val;return r},{});const target=['__Secure-1PSID','__Secure-1PSIDTS'];const out=target.map(n=>({name:n,value:c[n]}));prompt('Copy JSON:',JSON.stringify(out));})();") (harness-log "PLATFORM GUIDE: Chrome/Firefox/Safari all support Bookmarklets via 'Add Page' or 'New Bookmark'.") t) #+end_src ** Registration #+begin_src lisp (progn (defskill :skill-credentials-vault :priority 200 ; High priority, foundational :trigger (lambda (ctx) (eq (getf (getf ctx :payload) :sensor) :onboarding-request)) :probabilistic nil :deterministic (lambda (action ctx) (vault-onboard-gemini-web) action))) #+end_src * Phase E: Chaos (Verification) ** 1. Unit Tests (FiveAM) #+begin_src lisp (defpackage :org-agent-vault-tests (:use :cl :fiveam :org-agent)) (in-package :org-agent-vault-tests) (def-suite vault-suite :description "Tests for the Credentials Vault.") (in-suite vault-suite) (test test-masking (is (equal "sk-t...-key" (org-agent::vault-mask-string "sk-test-key"))) (is (equal "[REDACTED]" (org-agent::vault-mask-string "short")))) (test test-vault-persistence "Verify that setting a secret triggers a snapshot (mock check)." (let ((old-version (org-agent::org-object-version (gethash "root" *memory*)))) (org-agent:vault-set-secret :test "secret-val") (is (> (org-agent::org-object-version (gethash "root" *memory*)) old-version)))) #+end_src ** 2. Chaos Scenarios - *Scenario A (Vault Poisoning):* Inject a malformed session string and verify the `llm-gateway` detects the invalid format and returns a standardized error instead of crashing. - *Scenario B (Memory Wipe):* Clear `*vault-memory*` during runtime and verify the vault successfully re-hydrates from the Memory (or environment fallback). * Phase F: Memory (RCA) - *[2026-04-09 Thu]:* Consolidated `auth-api-key` and `auth-google-oauth` into this vault. Introduced mandatory masking for all credential-related logging.