Local-first compartmentalized LLM work.
Hand bounded, sanitized work to untrusted LLM workers without handing them your secrets.
Quick start | Beginner tutorial | Commands | Trust caveats | Architecture | Security
Skunky is a local-first compartmentalized LLM work system. It gives an operator a private broker workspace for handing bounded, sanitized work to untrusted LLM workers without exposing the project, identifiers, local paths, or raw files behind that work.
Skunky is built around one practical rule:
Sanitized packets go out. Hostile artifacts come back. Every disclosure gets checked, logged, and budgeted.
| Risk | Skunky control |
|---|---|
| A worker sees raw project files | Sources are parsed locally, encrypted, sanitized, and served only through packet-local handles. |
| A prompt injection in a source file becomes instructions | Source content crosses a subprocess parse boundary and is treated as data, not commands. |
| Real names, paths, URLs, or internal terms leak into packets | Sanitizers and validators scan worker-visible surfaces before use. |
| A handle gets reused or shared too broadly | Broker reads use packet-local, single-use, expiring handles. |
| Worker output smuggles active content or secrets back in | Submitted artifacts are treated as hostile data and scanned before sealing or quarantine. |
| "One small release" becomes cumulative disclosure | Releases are scored against a disclosure ledger and per-audience budgets. |
| Local records are edited after the fact | Audit logs are hash-chained, Ed25519-signed, and replayed by skunky verify. |
| If you want to... | Start with... |
|---|---|
| Try Skunky for the first time | Quick Start |
| Onboard real project data | Beginner tutorial |
| Understand the security model | Threat Model and Architecture |
| Check every CLI command | Commands |
| Review known limitations | Trust Caveats |
Skunky assumes the worker is hostile. A worker may be an LLM, script, model tool, or any untrusted process that tries to exfiltrate identifiers, obey instructions hidden in source content, or return unsafe artifacts.
Skunky trusts the operator boundary: the local machine, the skunky.local/
workspace, and the local keystore. Its job is to keep worker-visible material
bounded and sanitized, fail closed when checks do not pass, and leave a
verifiable record of what crossed the boundary.
For the complete model, see IMPLEMENTATION_SPEC.md and docs/ARCHITECTURE.md.
Requires Python 3.11+.
pip install -e . # from a clone
pip install -e .[dev] # with test dependenciesFor a local demo workspace:
skunky init
skunky mission create --name "Local demo"
skunky statusFor real project data, start with a passphrase-wrapped keystore:
export SKUNKY_KEYSTORE_PASSPHRASE="replace-this-with-5-plus-random-words"
skunky init --wrap-keystoreOn Windows PowerShell:
$env:SKUNKY_KEYSTORE_PASSPHRASE = "replace-this-with-5-plus-random-words"
skunky init --wrap-keystoreDo not copy the placeholder passphrase above. Skunky rejects short passphrases, obvious placeholders, and common weak values.
The typical workflow is:
- Create a mission and compartment.
- Add sensitive terms to the gazetteer.
- Add one source file.
- Create a worker packet.
- Let the worker read only assigned handles.
- Submit worker output as an artifact.
- Request and approve only reviewed releases.
- Run
skunky verify.
For a command-by-command walkthrough with PowerShell examples, use docs/BEGINNER_TUTORIAL.md.
| Command | Purpose |
|---|---|
skunky init |
Create the labeled skunky.local/ workspace. |
skunky init --wrap-keystore |
Create a workspace with a passphrase-wrapped local keystore. |
skunky status |
Show workspace summary. |
skunky keys wrap |
Passphrase-wrap an existing plaintext local keystore. |
skunky keys rotate |
Install a new root key and re-encrypt every local artifact. |
skunky verify |
Run deterministic acceptance gates and write reports under skunky.local/ci/reports/. |
| Command | Purpose |
|---|---|
skunky mission create |
Create a broker-private mission record. |
skunky compartment create |
Create a broker-private compartment. |
skunky gazetteer add |
Store a keyed HMAC fingerprint for a sensitive term; plaintext is never stored. |
skunky source add |
Encrypt a raw source, sanitize it, and write a sanitization manifest. |
| Command | Purpose |
|---|---|
skunky packet create |
Emit a worker-visible packet plus broker-private handle map and policy. |
skunky packet validate |
Re-validate an existing packet against schema, scanners, and forbidden terms. |
skunky broker read |
Resolve a packet-local single-use handle through policy and serve sanitized content. |
skunky artifact submit |
Ingest worker output as hostile data into encrypted storage. |
| Command | Purpose |
|---|---|
skunky release request |
Create a declassification request. |
skunky release approve |
Approve a reviewed request and update the disclosure ledger. |
skunky release ledger |
Show a decrypted ledger summary for the operator. |
| Control | What it does |
|---|---|
| Parser boundary | Parses source files in a subprocess before anything is persisted. |
| Deterministic sanitization | Uses gazetteer HMAC matching, regex scanners, and residual-risk stages, then re-scans sanitized output. |
| Single-use handles | Binds worker reads to one packet, one run, and one successful claim. |
| Fail-closed broker reads | Denies reads when a sanitized blob is missing, corrupt, expired, or unauthorized. |
| Disclosure ledger | Scores releases by granularity, timing precision, and linkage risk against per-audience budgets. |
| Audit replay | Replays hash chains, Ed25519 signatures, handle maps, policies, manifests, and ledger anchors. |
| Root-key rotation | Keeps retired root keys decrypt-only until all encrypted artifacts are rewritten. |
The MVP uses a local file keystore under skunky.local/keystore_refs/.
Plaintext key manifest mode is development-only. Do not use plaintext
manifest mode for real project data.
The only shipped keystore backend today is the local-file backend. OS keyring or hardware-backed storage should be added later as optional backends, not as half-wired dependencies in the current MVP.
Passphrase wrapping helps with:
- A copied key manifest alone is not enough to unlock the workspace.
- Existing plaintext workspaces can be migrated with
skunky keys wrap. - Skunky rejects short passphrases, obvious placeholders, and common weak values before wrapping or unlocking a keystore.
Passphrase wrapping does not solve:
- A weak or stolen passphrase.
- A process that is already unlocked.
- Broad local machine compromise.
- OS keyring or hardware-backed storage. Those are roadmap backends, not implemented dependencies yet.
Never copy skunky.local/ into worker packets, provider prompts, logs, or
shared artifacts, and never commit it to version control. The shipped
.gitignore excludes it for this repo.
See SECURITY.md for the full list of limitations and how to report vulnerabilities.
- docs/BEGINNER_TUTORIAL.md - absolute-beginner onboarding walkthrough for using Skunky with a first project
- docs/ARCHITECTURE.md - trust domains, dataflow, and workspace layout
- IMPLEMENTATION_SPEC.md - the complete system specification: threat model, schemas, folder structure, key management
- CONTRIBUTING.md - development setup and pull request guidelines
- SECURITY.md - reporting vulnerabilities and known limitations
- CHANGELOG.md - release history
Built local-first. Fails closed. Keeps receipts.
