Metadata-Version: 2.4
Name: tacit-citadel
Version: 0.2.0
Summary: Policy-driven JSON de-identification CLI
Requires-Python: <3.14,>=3.11
Requires-Dist: click>=8.4.1
Requires-Dist: logfire>=4.15.1
Requires-Dist: pydantic>=2.13.4
Requires-Dist: pyyaml>=6.0.3
Requires-Dist: rich>=14.2.0
Description-Content-Type: text/markdown

# Citadel

Citadel applies a YAML de-identification policy to JSON or JSONL records and
writes the transformed data to an explicit output path.

```bash
uvx --from tacit-citadel citadel policy.yaml input.json output.json
```

For local development from this checkout:

```bash
uv run citadel policy.yaml sample.jsonl sample.citadel.jsonl
```

The PyPI package is `tacit-citadel`; the installed console command is
`citadel`. A bare `uvx citadel` resolves the unrelated `citadel` package name
on PyPI.

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.

```yaml
version: 1
name: support-ticket-sanitizer
description: De-identification policy for customer support tickets.

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}}

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. If a rewrite fails or returns invalid JSON after three attempts,
Citadel logs an error, aborts the run, and does not write the output file.

## 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.

```yaml
- path: .account.id
  action: drop
```

### `fuzz_number`

Perturbs numeric values with either percentage or range mode.

```yaml
- path: .customer.contract_value_usd
  action: fuzz_number
  params:
    mode: percent
    max_percent: 3
    precision: 0
```

```yaml
- 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.

```yaml
- 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.

## Development

The implementation is intentionally contained in [run.py](run.py).

```bash
uv run pytest
uv run ruff check .
uv run ty check
```
