Metadata-Version: 2.4
Name: signato
Version: 0.12.2
Summary: Signato: information barriers for the age of AI, a deterministic and local confidentiality chamber (core: johari_guard)
License: Proprietary
Project-URL: Homepage, https://signato.ai
Project-URL: Documentation, https://github.com/Signato-ai/signato-integrations
Project-URL: Live demo, https://signato.ai/demo
Project-URL: Free 30-day local trial, https://signato.ai/trial
Classifier: Development Status :: 4 - Beta
Classifier: License :: Other/Proprietary License
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Classifier: Intended Audience :: Legal Industry
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: Topic :: Security
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE-DRAFT.md
Requires-Dist: PyYAML>=6.0
Provides-Extra: wizard
Requires-Dist: questionary>=2.0; extra == "wizard"
Provides-Extra: license
Requires-Dist: cryptography>=42; extra == "license"
Dynamic: license-file

# Signato

Information barriers for the age of AI: a deterministic, local confidentiality chamber for operations with mandatory internal walls.

Between two confidentiality perimeters, nothing crosses directly: every outbound event stops at the chamber and is checked before it passes. Signato is the name of the product and the pip package; `import signato` and `import johari_guard` expose the SAME API (1:1 alias since v0.4.0 — the historical technical name stays valid so integrations do not break). Public integration guides, the adapter contract, and synthetic examples: https://github.com/Signato-ai/signato-integrations

## The pain

A family office runs with several advisors on parallel workstreams: the succession lawyer, the bank renegotiating the debt, the investment advisor, the accountant. Each may only know their own front. Today that wall lives in the head of the principal and the team, and a single email, draft, or AI prompt to the wrong recipient breaks years of segregation. With AI assistants drafting communication at scale, the risk is no longer human but structural: the tool that writes faster also leaks faster.

## What it does

johari-guard turns the confidentiality matrix into executable configuration. You declare in YAML who must NOT know what (perimeters: people with aliases, blocked topics with keywords, enforcement mode) and the library checks every outbound event, crossing person against subject in the same text. A `hard` perimeter denies the operation; a `soft` perimeter allows it with a warning. Beyond the internal crossing, the egress-warn layer (v0.8.0) flags when a sensitive subject is heading to a destination outside the list of who may receive it: the internal wall plus a door alarm. Every decision stays auditable in append-only JSONL telemetry. Person detection by word boundary (no substring false positives), subject by keyword, all case-insensitive, with a configurable failure posture (fail-open for gradual rollout, fail-closed for critical paths). The included adapter plugs into Claude Code's PreToolUse hook contract; the core is host-agnostic.

## Quickstart

```python
from johari_guard import Guard

guard = Guard("config/exemplo-family-office.yaml")

v = guard.check(
    "Abel, we need to talk about the purchase offer for the company.",
    target="draft-email-abel.md",
)
if not v.allowed:
    print(f"blocked [{v.perimeter}]: {v.reason}")
```

As a Claude Code hook (settings.json, PreToolUse):

```bash
JOHARI_GUARD_CONFIG=/path/matrix.yaml \
JOHARI_GUARD_TELEMETRY=/path/telemetry.jsonl \
python3 -m johari_guard.adapters.claude_code
```

## Battle-tested

Extracted from a mechanism with real telemetry and a suite of 55 scenarios in production since 05/2026, protecting confidentiality perimeters in a real multi-advisor operation. This package is the machinery, fully separated from the data: the example matrix is 100% synthetic and the library knows no real perimeter.

Since 2026-07-03 the originating production runs ON this library (roadmap phase 2): the person×subject detection engine of the real mechanism is this package's `Guard`, with the real matrix delivered as external config — continuous dogfooding, a 55-scenario regression gate on every change, and a post-release telemetry observation window.

## CLI signato

After `pip install .`, the `signato` command is available with 11 subcommands:

```bash
# guided onboarding (v0.5): a business-language interview that generates a
# matrix + derived scenarios, runs the lint, and closes with a real block
# in front of you. Resumable; never overwrites without confirming; 100% local.
signato init
signato init --dir ./my-operation --fresh
signato init --import contacts.vcf     # assisted migration: suggests people from your export (.csv/.vcf)

# signato studio: the SAME onboarding in screens, in the browser (a 127.0.0.1 server from the
# package itself, pure stdlib; draft shared with init — start in one, finish in the other)
signato studio

# see in 1 minute how the library works (100% synthetic data)
signato demo

# check text or a file — exit 1 if BLOCK, 0 if ALLOW/SOFT, 2 on a configuration error
signato check --matrix matrix.yaml 'Dr. Araujo, purchase offer for the company...'
signato check --matrix matrix.yaml --file email.txt

# Retroactive Exposure Scan: point it at a local mailbox export (.eml/.txt/.mbox
# file, or a directory of them) and it checks every message against the matrix,
# streaming — never loads the whole mailbox in memory. It is a REPORT, not a
# gate (exit 0 even when exposure is found; non-zero only on a real I/O error).
# It never prints the protected content: only aggregate counts by perimeter,
# the same shape as `signato report`. Tolerant of dirty real-world mail
# (mismatched charset, malformed headers, unescaped mbox delimiters, forwarded
# messages nested as message/rfc822) — a message that cannot be parsed cleanly
# is flagged `degraded` and still counted, never dropped silently. Attachments
# are read as metadata only (filename/type/approximate size); the binary
# payload itself is never decoded. `.pst` is out of scope for the parser
# itself — convert it to `.mbox` first (e.g. with libpst) or export `.eml`.
signato scan --matrix matrix.yaml exported-mailbox.mbox
signato scan --matrix matrix.yaml ./exported-inbox/

# validate the matrix structure (coded anti-patterns: alias with notes, generic keyword, duplicate key, empty triggers, a subject that does not block its own name, and more)
signato lint --matrix matrix.yaml

# runner for declarative YAML scenarios — PASS/FAIL output per scenario
signato test --matrix matrix.yaml --scenarios scenarios.yaml

# JSONL telemetry summary with date filters and a per-mode/perimeter breakdown
signato report --telemetry events.jsonl --inicio 2026-01-01 --fim 2026-06-30
# value report for the closed month, compared with the previous month (v0.7)
signato report --telemetry events.jsonl --mes 2026-06

# matrix snapshot + diff vs the previous trail + append-only record for audit
signato attest --matrix matrix.yaml --log attestations.jsonl
signato attest --matrix matrix.yaml --log attestations.jsonl --dry-run

# what-if BEFORE changing the matrix (v0.4): replay of history and/or scenario diff
signato simulate --telemetry events.jsonl --remove-perimeter wall-x --soften wall-y
signato simulate --matrix current.yaml --proposed new.yaml --scenarios scenarios.yaml
# exit 1 when the proposal LOOSENS any verdict (good for a matrix-change CI)

# license (commercial layer, SOFT enforcement): status auto-provisions a local 30-day trial;
# no license state turns off the wall (signato check never consults the license)
signato license status
signato license activate --token SIG1.<payload>.<signature>   # installs a paid license verified offline
```

Test scenarios format (`scenarios.yaml`):

```yaml
matrix: path/to/matrix.yaml   # optional if passed via --matrix
scenarios:
  - name: block-advisor-a-with-sale-topic
    payload: "Advisor A, purchase offer approved."
    expected: block
  - name: allow-subject-without-person
    payload: "Purchase offer approved by the board."
    expected: allow
  - name: warn-advisor-c-with-wealth
    payload: "Advisor C, investment portfolio available."
    expected: soft
```

## Installation and dependencies

PyYAML and stdlib, nothing else. The library is standalone: it imports nothing from outside its own package.

```bash
pip install signato                     # from PyPI (recommended)
pip install .                           # from a source checkout, same result
pip install ".[wizard]"                 # optional: arrows/checkbox in `signato init` (questionary)
pip install ".[license]"                # optional: verifies a signed paid license (cryptography); trial and core do not need it
signato demo                            # quick smoke test
python -m unittest discover -s tests    # full suite (652 tests — CLI + guard + governance + wizard + studio + templates/report + license + billing)
```

> Dev note (interpreter): the suite requires PyYAML (and, for the optional tests, `cryptography`/`questionary`).
> Use a Python that has them installed; a "bare" interpreter without PyYAML gives an import error, not a real
> product failure. The `studio` tests bind to 127.0.0.1 and are skipped in an environment that denies bind.

## API

`Guard.check(text, target) -> Verdict` is the path with a failure posture (fail-open/fail-closed). `Guard.egress_check(text, target, channel) -> Verdict` (v0.8.0) is the outbound surveillance layer: it warns (SOFT, `mode="egress-soft"`, never blocks) when a sensitive subject goes to a destination outside the trusted list, with the config in the matrix's `egress` section (`trusted_recipients`, `generic_domains`). For hosts with even more custom layers, the pure helpers `Guard.people_in(text, target)` and `Guard.topic_hits(content, topics)` expose the raw detection — no telemetry, and an unavailable matrix raises `MatrixError` (the failure posture stays with the caller).

Governance in the core (v0.4 — `import signato` is a 1:1 alias of `johari_guard`):

- **Audited wall-crossing**: `Guard(config, override_store=OverrideStore("overrides.jsonl"))` — a HARD deny whose violations are ALL covered by live overrides becomes an audited allow (`Verdict.is_override`, `Verdict.override_ids`, telemetry `mode="override"`). Overrides are created by `OverrideStore.create(pessoa_alias, topico, justificativa, escopo, ttl_hours, criado_por)` (the API keeps its original identifiers): justification required, scope `proximo_envio` (single-use) or `ate_ts`, a 72h cap validated on creation AND on read (a forged record in the file does not escape). Append-only JSONL trail, event-sourcing.
- **Attestation as an API**: `attest(matrix_path, log_path)` → canonical snapshot + hash + diff vs the last entry + a record in the trail (`AttestResult`); `history(log_path)` reads the trail. The regulated-market pattern (periodically attesting that the standing wall is the intended one), callable by cron.
- **What-if simulation**: `replay(events, remove_perimeters, soften_perimeters)` reprocesses historical verdicts under the proposed change; `scenario_diff(current, proposed, scenarios)` runs the declarative suite on both matrices and returns each verdict that changes (with a `loosened` flag). Honest limit: adding a new keyword requires scenarios — the telemetry does not store content (privacy by design).
- **Embeddable onboarding** (v0.5): `run_wizard(io=None, out_dir=".", fresh=False)` runs the same interview as `signato init` from inside a host; the `io` parameter accepts a custom backend (the test one runs the whole interview without a TTY). `derive_scenarios(matrix_dict)` derives and VALIDATES scenarios from any matrix programmatically.

## Roadmap

Phase 2 (COMPLETED 2026-07-04): the originating internal mechanism imports this library (production is the first client); telemetry gate + internal suite of 55 scenarios passed, staged rollout in production with wall-crossing, accent normalization, and a semantic egress layer on the host.

Phase 3: adapters for other hosts (email gateway, chat/LLM middleware, document CI), keeping the person×subject core intact and the Verdict contract as the single interface.
