os-keychain
# OS Keychain Skill
What this skill does
# OS Keychain Skill
---
name: os-keychain
version: 1.1.0
domain: security/credential-storage
risk_level: HIGH
languages: [python, typescript, rust, go]
frameworks: [keyring, security-framework, libsecret]
requires_security_review: true
compliance: [GDPR, HIPAA, PCI-DSS, SOC2]
last_updated: 2025-01-15
---
> **MANDATORY READING PROTOCOL**: Before implementing credential storage, read `references/advanced-patterns.md` for cross-platform patterns and `references/security-examples.md` for platform-specific implementations.
## 1. Overview
### 1.1 Purpose and Scope
This skill provides secure credential storage using OS-native keychain services:
- **Windows**: Credential Manager (DPAPI-backed)
- **macOS**: Keychain Services (Secure Enclave integration)
- **Linux**: Secret Service API (GNOME Keyring, KWallet)
### 1.2 Risk Assessment
**Risk Level**: HIGH
**Justification**:
- Master keys and sensitive credentials stored
- Compromise exposes all dependent systems
- Platform API misuse leads to insecure storage
- Privilege escalation can access all credentials
**Attack Surface**:
- Inter-process communication (D-Bus, XPC)
- Access control misconfigurations
- Memory disclosure attacks
- Privilege escalation to access keychain
## 2. Core Principles
1. **TDD First** - Write tests before implementing credential operations
2. **Performance Aware** - Cache credentials, batch operations, minimize keychain calls
3. **Platform-native storage** - Use OS keychain services for all credentials
4. **Access isolation** - Unique service names prevent cross-contamination
5. **Secure by default** - Reject insecure backends automatically
6. **Cross-platform support** - Unified API across Windows, macOS, Linux
### 2.1 Security Principles
- **NEVER** store secrets in environment variables or files
- **NEVER** log credential values or access patterns with identifiers
- **ALWAYS** use platform-native keychain services
- **ALWAYS** validate application identity before credential access
- **ALWAYS** use unique service names per credential type
## 3. Implementation Workflow (TDD)
### Step 1: Write Failing Test First
```python
import pytest
from unittest.mock import MagicMock, patch
class TestCredentialStoreOperations:
"""TDD tests for credential store - write these FIRST."""
def test_store_credential_success(self):
"""Test storing a credential in keychain."""
# Arrange
store = SecureCredentialStore("test-service")
# Act
store.store("api-key", "sk-test-12345")
# Assert
assert store.exists("api-key") is True
assert store.retrieve("api-key") == "sk-test-12345"
def test_retrieve_nonexistent_raises_keyerror(self):
"""Test retrieving nonexistent credential raises KeyError."""
store = SecureCredentialStore("test-service")
with pytest.raises(KeyError, match="Credential not found"):
store.retrieve("nonexistent-key")
def test_delete_removes_credential(self):
"""Test deletion completely removes credential."""
store = SecureCredentialStore("test-service")
store.store("temp-key", "temp-value")
store.delete("temp-key")
assert store.exists("temp-key") is False
def test_credential_isolation_between_namespaces(self):
"""Test credentials are isolated by namespace."""
store1 = SecureCredentialStore("namespace-a")
store2 = SecureCredentialStore("namespace-b")
store1.store("shared-key", "value-a")
store2.store("shared-key", "value-b")
assert store1.retrieve("shared-key") == "value-a"
assert store2.retrieve("shared-key") == "value-b"
def test_rejects_insecure_backend(self):
"""Test rejection of insecure keyring backends."""
import keyring
from keyring.backends import null
original = keyring.get_keyring()
try:
keyring.set_keyring(null.Keyring())
with pytest.raises(RuntimeError, match="Insecure"):
SecureCredentialStore("test")
finally:
keyring.set_keyring(original)
```
### Step 2: Implement Minimum to Pass
```python
import keyring
from keyring.errors import KeyringError
import logging
logger = logging.getLogger(__name__)
class SecureCredentialStore:
"""Minimal implementation to pass tests."""
SERVICE_PREFIX = "com.jarvis.assistant"
def __init__(self, namespace: str):
self._service = f"{self.SERVICE_PREFIX}.{namespace}"
self._verify_backend()
def _verify_backend(self):
backend = keyring.get_keyring()
backend_name = type(backend).__name__
insecure = ['PlaintextKeyring', 'NullKeyring', 'ChainerBackend']
if backend_name in insecure:
raise RuntimeError(f"Insecure keyring backend: {backend_name}")
def store(self, key: str, secret: str) -> None:
keyring.set_password(self._service, key, secret)
def retrieve(self, key: str) -> str:
secret = keyring.get_password(self._service, key)
if secret is None:
raise KeyError(f"Credential not found: {key}")
return secret
def delete(self, key: str) -> None:
keyring.delete_password(self._service, key)
def exists(self, key: str) -> bool:
return keyring.get_password(self._service, key) is not None
```
### Step 3: Refactor with Performance Patterns
After tests pass, add caching and logging (see Performance Patterns section).
### Step 4: Run Full Verification
```bash
# Run all tests with coverage
pytest tests/security/test_keychain.py -v --cov=src/security/keychain
# Run security-specific tests
pytest tests/security/ -k "keychain or credential" -v
# Verify no credential leaks in logs
grep -r "sk-\|password\|secret" logs/ && echo "FAIL: Credentials in logs"
```
## 4. Performance Patterns
### 4.1 Credential Caching
```python
# BAD: Repeated keychain access
class SlowCredentialStore:
def get_api_key(self):
return keyring.get_password(self._service, "api-key") # Slow IPC every call
# GOOD: In-memory cache with TTL
from functools import lru_cache
from threading import Lock
import time
class CachedCredentialStore:
def __init__(self, namespace: str, cache_ttl: int = 300):
self._service = f"com.jarvis.{namespace}"
self._cache: dict[str, tuple[str, float]] = {}
self._lock = Lock()
self._ttl = cache_ttl
def retrieve(self, key: str) -> str:
with self._lock:
if key in self._cache:
value, timestamp = self._cache[key]
if time.time() - timestamp < self._ttl:
return value
secret = keyring.get_password(self._service, key)
if secret is None:
raise KeyError(f"Credential not found: {key}")
self._cache[key] = (secret, time.time())
return secret
def invalidate(self, key: str = None):
with self._lock:
if key:
self._cache.pop(key, None)
else:
self._cache.clear()
```
### 4.2 Batch Operations
```python
# BAD: Individual keychain calls
def load_all_credentials():
db_pass = keyring.get_password("jarvis", "db-password")
api_key = keyring.get_password("jarvis", "api-key")
secret = keyring.get_password("jarvis", "encryption-key")
return db_pass, api_key, secret # 3 separate IPC calls
# GOOD: Batch loading with single initialization
class BatchCredentialLoader:
def __init__(self, namespace: str, keys: list[str]):
self._service = f"com.jarvis.{namespace}"
self._credentials = self._load_batch(keys)
def _load_batch(self, keys: list[str]) -> dict[str, str]:
"""Load multiple credentials in optimized batch."""
result = {}
for key in keys:
value = keyring.get_password(self._service, key)
if value:
result[key] = value
return result
defRelated in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.