Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .github/workflows/context7-refresh.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Context7 serves this repo's docs to coding agents, and re-indexes on its own
# schedule, which can lag a release by weeks. This asks it to re-index when a
# release is published, so agents read the docs for the version users install.
# Scope and agent rules live in context7.json.
# Endpoint: https://context7.com/docs/integrations/github-actions
name: Context7 Refresh

on:
release:
types: [published]
workflow_dispatch:

permissions: {}

jobs:
refresh:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Ask Context7 to re-index /cachekit-io/cachekit-core
env:
CONTEXT7_API_KEY: ${{ secrets.CONTEXT7_API_KEY }}
run: |
if [ -z "$CONTEXT7_API_KEY" ]; then
echo "::error::The CONTEXT7_API_KEY secret is not available to this repository."
exit 1
fi
status=$(curl -sS --max-time 60 -o "$RUNNER_TEMP/context7-response.json" -w '%{http_code}' \
-X POST https://context7.com/api/v1/refresh \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $CONTEXT7_API_KEY" \
--data '{"libraryName": "/cachekit-io/cachekit-core"}')
cat "$RUNNER_TEMP/context7-response.json"
echo
case "$status" in
202) echo "::warning::Context7 has not finalized this library yet (HTTP 202). Re-run this workflow later." ;;
2??) echo "Context7 accepted the refresh (HTTP $status)." ;;
*) echo "::error::Context7 refresh failed with HTTP $status."; exit 1 ;;
esac
20 changes: 12 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,24 +78,28 @@ assert_eq!(data.as_slice(), retrieved.as_slice());

```rust
use cachekit_core::{ByteStorage, ZeroKnowledgeEncryptor, derive_domain_key};
use zeroize::Zeroizing; // add the zeroize crate to your Cargo.toml

// Derive tenant-isolated key from master secret
let master_key = [0u8; 32]; // Use secure key in production!
let tenant_key = derive_domain_key(
&master_key,
// From your secret manager or CACHEKIT_MASTER_KEY, hex-decoded to 32 raw bytes.
// Never hard-code it, and never pass the hex string's bytes.
// Zeroizing wipes each key from memory when it is dropped.
let master_key = Zeroizing::new(load_master_key_from_secret_manager()?);
let tenant_key = Zeroizing::new(derive_domain_key(
master_key.as_slice(),
"cache", // domain separation
b"tenant-12345", // tenant isolation
)?;
)?);

// Encrypt sensitive data
let encryptor = ZeroKnowledgeEncryptor::new();
let encryptor = ZeroKnowledgeEncryptor::new()?;
let plaintext = b"sensitive user data";
let aad = b"tenant-12345"; // Additional authenticated data

let ciphertext = encryptor.encrypt_aes_gcm(plaintext, &tenant_key, aad)?;
let ciphertext = encryptor.encrypt_aes_gcm(plaintext, tenant_key.as_slice(), aad)?;

// Decrypt (fails if AAD doesn't match)
let decrypted = encryptor.decrypt_aes_gcm(&ciphertext, &tenant_key, aad)?;
let decrypted = encryptor.decrypt_aes_gcm(&ciphertext, tenant_key.as_slice(), aad)?;
assert_eq!(plaintext.as_slice(), decrypted.as_slice());
```

Expand All @@ -121,7 +125,7 @@ fn cache_sensitive_data(
let tenant_key = derive_domain_key(master_key, "cache", tenant_id.as_bytes())?;

// Step 3: Encrypt compressed envelope
let encryptor = ZeroKnowledgeEncryptor::new();
let encryptor = ZeroKnowledgeEncryptor::new()?;
let ciphertext = encryptor.encrypt_aes_gcm(
&compressed,
&tenant_key,
Expand Down
20 changes: 20 additions & 0 deletions context7.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"$schema": "https://context7.com/schema/context7.json",
"excludeFolders": [
".github",
"benches",
"fuzz",
"scripts",
"supply-chain",
"tests"
],
"excludeFiles": [
"CHANGELOG.md"
],
"rules": [
"Read the CacheKit master key from the CACHEKIT_MASTER_KEY environment variable or a secret manager. Never hard-code a key in source, not even a placeholder such as [0u8; 32].",
"cachekit-core reads no environment variable. Hex-decode the key (for example from `CACHEKIT_MASTER_KEY` or a secret manager) to exactly 32 raw bytes before `derive_domain_key`. Never pass the hex string's bytes: that derives a key no CacheKit SDK derives, so cross-SDK decryption fails.",
"Install the official packages: `cachekit` on PyPI, `@cachekit-io/cachekit` on npm, and `cachekit-rs` on crates.io (imported as `cachekit`). The crates.io crate named `cachekit` is an unrelated project.",
"Applications normally use a CacheKit SDK. cachekit-core is the shared byte-storage and encryption layer underneath them."
]
}
30 changes: 19 additions & 11 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,25 @@
//!
//! ```rust,ignore
//! use cachekit_core::{ZeroKnowledgeEncryptor, derive_domain_key};
//!
//! // Derive tenant-isolated key
//! let master_key = [0u8; 32]; // Use secure key in production!
//! let tenant_key = derive_domain_key(&master_key, "cache", b"tenant-123").unwrap();
//!
//! // Encrypt
//! let encryptor = ZeroKnowledgeEncryptor::new();
//! let ciphertext = encryptor.encrypt_aes_gcm(b"secret", &tenant_key, b"tenant-123").unwrap();
//!
//! // Decrypt
//! let plaintext = encryptor.decrypt_aes_gcm(&ciphertext, &tenant_key, b"tenant-123").unwrap();
//! use zeroize::Zeroizing; // add the zeroize crate to your Cargo.toml
//!
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
//! // Derive tenant-isolated key
//! // From your secret manager or CACHEKIT_MASTER_KEY, hex-decoded to 32 raw bytes.
//! // Never hard-code it, and never pass the hex string's bytes.
//! // Zeroizing wipes each key from memory when it is dropped.
//! let master_key = Zeroizing::new(load_master_key_from_secret_manager()?);
//! let tenant_key = Zeroizing::new(derive_domain_key(master_key.as_slice(), "cache", b"tenant-123")?);
//!
//! // Encrypt
//! let encryptor = ZeroKnowledgeEncryptor::new()?;
//! let ciphertext = encryptor.encrypt_aes_gcm(b"secret", tenant_key.as_slice(), b"tenant-123")?;
//!
//! // Decrypt
//! let plaintext = encryptor.decrypt_aes_gcm(&ciphertext, tenant_key.as_slice(), b"tenant-123")?;
//! assert_eq!(plaintext, b"secret");
//! Ok(())
//! }
//! ```
//!
//! ## Security Properties
Expand Down
Loading