api-security
Use for authorized security assessment of REST, GraphQL, WebSocket, or SOAP APIs, including discovery, authentication,…
Required before any reply that touches KSafe (`by ksafe(...)`, `ksafe.get/put`, :ksafe-compose, :ksafe-biometrics), even a 'can KSafe do X?' question or a one-line change that looks like plain Kotlin. KSafe's signatures, defaults and overloads changed in recent releases, so
Claude Code
$ npx -y skills add ioannisa/KSafe --skill ksafe --agent claude-code
Codex
$ npx skills add ioannisa/KSafe --skill ksafe -a codex -y
Cursor
$ npx skills add ioannisa/KSafe --skill ksafe -a cursor -y
Gemini CLI
$ npx skills add ioannisa/KSafe --skill ksafe -a gemini-cli -y
GitHub Copilot
$ npx skills add ioannisa/KSafe --skill ksafe -a github-copilot -y
OpenCode
$ npx skills add ioannisa/KSafe --skill ksafe -a opencode -y
Windsurf
$ npx skills add ioannisa/KSafe --skill ksafe -a windsurf -y
name: ksafe description: | Required before any reply that touches KSafe (`by ksafe(...)`, `ksafe.get/put`, :ksafe-compose, :ksafe-biometrics), even a 'can KSafe do X?' question or a one-line change that looks like plain Kotlin. KSafe's signatures, defaults and overloads changed in recent releases, so answers from memory give wrong code. Typical: making one value unencrypted, passing a KSerializer for a generic T, faking KSafe in commonTest, compile errors, crashes, Desktop packaging. Also use when KSafe is unnamed but Kotlin/Compose Multiplatform code must keep secrets or settings on device: tokens, PINs, passwords, DB passphrases, encrypted prefs, a testable local data source, choosing or replacing a KMP secure-storage library (EncryptedSharedPreferences, DataStore, KVault, Multiplatform Settings), Face ID/fingerprint/Windows Hello gating, key rotation, proving keys sit in StrongBox/Secure Enclave/Keychain. Skip storage work with no KMP target and no KSafe (pure Swift, Android-only, browser).
You are about to write or modify code that uses **KSafe**: a one-API encrypted key-value store covering Android, iOS, native macOS, JVM Desktop, Kotlin/WasmJS, and Kotlin/JS. Encrypted values use AES-GCM. Keep **payload encryption**, **durable key custody**, and the **working key in process memory** conceptually separate: the secure paths protect a long-lived key or KEK in a platform vault, while documented software fallbacks can keep key material in a permission-protected file. Use `protectionInfo` to report the route that was actually achieved.
This skill is self-contained — it covers everything you need to **set up** and **use** KSafe correctly. Always prefer the **property delegate** as the default API.
**The single most important fact: KSafe is encrypted by default.** `ksafe(value)` encrypts. You opt *out* for non-secret values with `mode = KSafeWriteMode.Plain`.
---
| Platform | Default encrypted route | `HARDWARE_ISOLATED` upgrade / fallback | |---|---|---| | Android | Relaxed: non-exportable Keystore KEK wraps a DEK stored as ciphertext; the unwrapped DEK is cached in RAM. Strict unlock mode performs payload operations in Keystore. | Per-entry StrongBox when available; normal Keystore fallback otherwise. | | iOS / native macOS | AES key stored in Keychain, loaded into the app for CryptoKit payload operations. | Secure Enclave EC key wraps a per-entry AES DEK; ordinary Keychain fallback otherwise. Simulator-only entitlement failure can use a reported software file fallback. | | JVM Desktop | AES key protected by Windows DPAPI, macOS login Keychain, or Linux Secret Service, then loaded for JCE payload operations. | No stronger common tier. A reported permission-protected file fallback is used only where no usable OS vault exists or the user explicitly opts out. | | WasmJS / JS | Non-extractable WebCrypto AES `CryptoKey` in IndexedDB. | No stronger tier; outside a secure context encrypted operations are non-operational rather than silently written plain. |
When a stronger tier is absent (for example no StrongBox, no Secure Enclave, or no supported desktop OS vault), KSafe **degrades to the documented next-best path and reports the degrade** through `KSafe.protectionInfo`. If a real JVM OS vault exists but is temporarily unreachable, KSafe fails closed instead of inventing a replacement software key. Never trade operability for silent data loss.
---
// commonMain (or Android-only) build.gradle.kts
implementation("eu.anifantakis:ksafe:<latest>") // core
implementation("eu.anifantakis:ksafe-compose:<latest>") // optional: Compose state
implementation("eu.anifantakis:ksafe-biometrics:<latest>") // optional: biometric prompts`kotlinx-serialization-json` comes transitively — don't add it yourself. If you store `@Serializable` classes, apply the kotlin-serialization plugin in your app.
// Android — pass applicationContext (NOT an Activity context — it leaks) val ksafe = KSafe(applicationContext) // iOS / macOS / JVM / WasmJS / JS — no context val ksafe = KSafe() val ksafe = KSafe(fileName = "auth") // isolated named instance
Full factory parameters (all platforms except where noted):
KSafe(
context: Context, // Android ONLY — applicationContext
fileName: String? = null, // null = default instance; else isolates storage
lazyLoad: Boolean = false, // ignored on web
memoryPolicy: KSafeMemoryPolicy = KSafeMemoryPolicy.LAZY_PLAIN_TEXT,
config: KSafeConfig = KSafeConfig(),
securityPolicy: KSafeSecurityPolicy = KSafeSecurityPolicy.Default,
baseDir: File? = null, // JVM/Android custom dir; iOS uses `directory: String?`
)
KSafeConfig(
aesKeySize: KSafeAesKeySize = KSafeAesKeySize.BITS_256, // BITS_128 or BITS_256; every platform
requireUnlockedDevice: Boolean = false, // default unlock policy for encrypted writes
json: Json = KSafeDefaults.json, // custom serialization
appNamespace: String? = null, // multi-app isolation (see below)
keyRotationPolicy: KSafeKeyRotationPolicy = KSafeKeyRotationPolicy.Never, // see Key rotation
keyRotationRetryAttempts: Int = 3, // next-instance retries for skipped work; 0 = off
)Encryption adds per-value overhead (AES-GCM + JSON envelope; ~µs since 2.1.2, but never free). For non-secret data — theme, last screen, UI flags — that overhead is wasted. Since 3.1.0 the recommended pattern is **one store, typed views**: `KSafePlain` / `KSafeEncrypted` / `KSafeHardwareIsolated` wrap an existing instance and freeze the write mode at the type level, so no call site carries a `mode =` argument the author can forge
Encrypted by default. Plain (unencrypted) when needed. Persist variables, Compose State, StateFlow, and serializable objects across Android, iOS, macOS, Desktop, and Web Easy to use by design — plus key rotation, cross-platform biometrics, and an encryption
Use for authorized security assessment of REST, GraphQL, WebSocket, or SOAP APIs, including discovery, authentication,…
Extract and decrypt Windows DPAPI-protected secrets (Credential Manager, browser logins/cookies, Wi-Fi credentials, KeePass keys)…
Clean and finalize authorized natural-language text intended for readers by auditing suspicious invisible Unicode and rewriting…