Privacy Configs and Skill Privacy Extraction/Restore
This page explains OpenViking privacy configs and how they work with skill write-time extraction and read-time restore.
Goal
Privacy configs separate sensitive values (such as api_key, token, base_url) from skill body content to reduce plaintext storage of recognized values in SKILL.md, with version lookup and switching. Extraction relies on a model and replaces only recognized, matched spans; it does not guarantee discovery of every sensitive value.
Core goals:
- Auto-extract sensitive values at write time and replace with placeholders
- Auto-restore values from the active config at read time
- Provide version query, switching, and auditability
Core Objects and Storage Layout
Privacy configs are keyed by category + target_key.
category: config category (currently skill usesskill)target_key: target identifier (usually the skill name)
Storage layout in user space:
viking://user/{user_id}/privacy/{category}/{target_key}/
├── .meta.json # metadata (active_version/latest_version/labels, etc.)
├── current.json # active version snapshot
└── history/
├── version_1.json
├── version_2.json
└── ...current.json and history/version_x.json are full values snapshots.
Version Semantics
upsert
- Each upsert treats incoming
valuesas the candidate snapshot - If
valuesis identical to currentvalues, no new version is created - Otherwise a new version is created and activated
- New keys are allowed (no unknown-key rejection)
valuesis a full snapshot, not a patch; omitted old keys are absent from the new version
activate
- Sets a historical version as active (writes back to
current.json) - Updates
active_versionin.meta.json - Does not rotate or revoke credentials at an external service; it only selects the values OpenViking uses
Skill Privacy Extraction (Write Path)
When adding a skill via add_skill, OpenViking runs extraction + placeholderization.
add_skill
-> SkillProcessor._sanitize_skill_privacy
-> extract_skill_privacy_values (LLM extracts values)
-> placeholderize_skill_content_with_blocks (replace plaintext with placeholders)
-> privacy.upsert(category="skill", target_key=skill_name, values=...)
-> write placeholderized SKILL.mdKey Points
- Extraction source: model returns JSON;
valuesis used as privacy key-value pairs. - Content replacement: matched plaintext is replaced by placeholders:
{{ov_privacy:skill:{skill_name}:{field_name}}}
- Block mapping captured:
original_content_blocksreplacement_content_blocks
- Persisted content: matched spans become placeholders; unmatched content remains unchanged. This processes the skill body, not every attached file.
Skill Restore on Read (Load Path)
When reading SKILL.md, FSService.read attempts placeholder restoration automatically.
fs.read(uri)
-> get_skill_name_from_uri(uri)
-> privacy.get_current(category="skill", target_key=skill_name)
-> restore_skill_content(content, skill_name, current.values)URI Matching
Current matching is suffix-based: /skills/{name}/SKILL.md, so it supports user-scoped skill paths such as:
viking://~/skills/{name}/SKILL.mdviking://user/{user_id}/skills/{name}/SKILL.md
restore Rules
restore_skill_content behavior:
Placeholder exists in content and value exists and is non-empty
-> replace directly.Placeholder exists in content but value is missing/empty
-> keep placeholder; add item tounresolved_entries.Config key is non-empty but not referenced by any placeholder in content
-> add to extra-config notice (Configured but not referenced in content).If
unresolved_entriesor extra-config entries exist, append notice block:[Privacy Config Notice]Missing config: ...(if any)Configured but not referenced in content: ...(if any)
Current implementation only runs restore when a
currentprivacy config exists for that skill. If no current config exists, no notice is appended.
read returns restored values, and extra-config notices also include values. Privacy APIs and historical snapshots can return full values as well. This feature does not redact responses or encrypt storage; see Storage Encryption for encryption.
Relationship with CLI/API
- Management plane: Privacy API/CLI for versions (query, write, rollback)
- Content plane:
readautomatically restores placeholders - Separation of concerns: content files hold placeholders; privacy service holds sensitive values and versions
PrivacySanitizer does not infer sensitivity from field names. String values in the active Privacy catalog participate in automatic content scanning and reference replacement only when they contain at least 6 Unicode characters. Shorter values can still be stored, referenced, and explicitly resolved, but are excluded from global substring replacement to reduce false positives in ordinary text. Python owns catalog loading, reference generation, and cache policy; matcher construction, leftmost-longest matching, and text rewriting run in RAGFS's Rust Aho-Corasick implementation through a reusable PyO3 matcher that releases the GIL while scanning. The matcher catalog is cached per user and process-local revision; local changes invalidate it immediately, and a 3600-second TTL bounds staleness after changes made by another worker or node.
Using a GitHub Personal Access Token (PAT) as an example:
# Create or rotate the PAT; credential/github is the target and pat is the field
ov privacy upsert credential github --field-pat 'github_pat_...'
# Inspect current targets and fields without exposing values
ov privacy tree --category credential --references
# Omit the field when pat is the target's only field
ov privacy reference credential github
ov privacy resolve credential github
# Equivalent explicit-field forms
ov privacy reference credential github pat
ov privacy resolve credential github pat
ov privacy resolve '{{ov_privacy:alice:viking://~/privacy/credential/github#/values/pat}}'
# Inspect/activate history, or remove pat (the final field also removes the target)
ov privacy versions credential github
ov privacy activate credential github 1
ov privacy delete credential github pat --yes
# Scan one explicit URI; the default only reports, while --fix replaces plaintext PATs
ov privacy scan --uri viking://~/resources/project-aBenefits
- Reduces plaintext sensitive data exposure in skill content
- Version history supports configuration changes after external credential rotation
- Transparent to callers:
readreturns restored executable skill text - Notice block improves troubleshooting when config is incomplete or over-provisioned
Related Documentation
- Skills - Skill write/read API
- Privacy API - Privacy config endpoints
- Context Extraction - Main extraction pipeline
- Architecture Overview - Layering and module relationships