Citadel
Citadel applies a YAML de-identification policy to JSON or JSONL records and writes the transformed data to an explicit output path.
uvx tacit-citadel policy.yaml input.json output.json
For local development from this checkout:
uv run tacit-citadel policy.yaml sample.jsonl sample.citadel.jsonl
The PyPI package and installed console command are both tacit-citadel.
Project-specific policies can live with the dataset they are used for. The
checked-in policy.yaml and sample.jsonl are generic support-ticket examples
for local smoke tests.
Policy Shape
Policies have deterministic path-based rules and an optional whole-record rewrite step.
version: 1
name: support-ticket-sanitizer
description: De-identification policy for customer support tickets.
validators:
- path: .customer.contract_value_usd
action: number_range
params:
min: 1
max: 100000000
rewrite:
backend: codex exec
sandbox: read-only
system_prompt: You are a conservative de-identification rewriter.
user_prompt: |
Rewrite the INPUT JSON object.
INPUT JSON
{{content}}
preserve:
- path: .customer.contract_value_usd
required: false
rules:
- path: .account.id
action: drop
rewrite.backend must be codex exec or claude -p. Rewrites always receive
the whole record after deterministic rules have run and must return a complete
JSON object. rewrite.preserve selectors snapshot values after deterministic
rules and require the same values after the rewrite; required defaults to
true. If a rewrite fails, returns invalid JSON, or changes a preserved value
after three attempts, Citadel logs an error, aborts the run, and does not write
the output file.
Validators
Validators run before deterministic rules and rewrite. If a record fails any validator, Citadel omits that record from the output.
number_range requires every matched value to be numeric and within inclusive
min / max bounds. At least one bound is required. required defaults to
true; a missing required validator path skips the record.
validators:
- path: .intake_details.weight
action: number_range
params:
min: 30
max: 300
Rules
path is a small jq-like selector. It supports dotted object fields, list
wildcards, numeric list indexes, quoted bracket fields, and simple
| select(.field == "value") / | select(.field != "value") filters.
required defaults to true. Use required: false for sparse paths.
drop
Deletes matched object fields.
- path: .account.id
action: drop
fuzz_number
Perturbs numeric values with either percentage or range mode.
- path: .customer.contract_value_usd
action: fuzz_number
params:
mode: percent
max_percent: 3
precision: 0
- path: .risk_score
action: fuzz_number
params:
mode: range
min_delta: -1
max_delta: 1
step: 1
date_offset
Replaces date or datetime strings with a day offset from an anchor date.
- path: .events[].timestamp
action: date_offset
required: false
params:
anchor_path: .reported_at
output: human_relative
Outputs are same day, N day later, N days later, N day ago, or
N days ago.
Inputs
Citadel accepts:
- a JSON object
- a JSON array of objects
- JSONL with one object per non-empty line
The output format matches the input shape. Skipped JSONL records are omitted,
skipped JSON array items are removed, and a skipped single JSON object writes
null.
Development
The implementation is intentionally contained in run.py.
uv run pytest
uv run ruff check .
uv run ty check
Metadata
Release files for tacit-citadel 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| tacit_citadel-0.3.0.tar.gz | 15.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| tacit_citadel-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 25.4 kB
Release files / tacit_citadel-0.3.0.tar.gz
| Download URL | tacit_citadel-0.3.0.tar.gz |
|---|---|
| Size | 15.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
cba2f1d5afc0301322b6b923600e80134c5766471f89fc11226e9969b7ed9aec
|
|
BLAKE2b-256 checksum How to use checksums |
d0dcc9751c8d3da45fedecb7b6a8d223504f23e323f88f3b70f3a3ba4eeffc4a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.24 {"installer":{"name":"uv","version":"0.9.24","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / tacit_citadel-0.3.0-py3-none-any.whl
| Download URL | tacit_citadel-0.3.0-py3-none-any.whl |
|---|---|
| Size | 10.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
da410a01681e469b80da5913651904422c01561d261e60196b907f2e79a5a5ea
|
|
BLAKE2b-256 checksum How to use checksums |
56d0289eeff792347a62b5fa41dab64a8815b43636f6c711c9220af58f95ac65
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.24 {"installer":{"name":"uv","version":"0.9.24","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|