Metadata-Version: 2.3
Name: titto
Version: 0.4.0
Summary: Delete tenant-scoped MySQL data by walking foreign-key relationships
Author: Florian Daude
Author-email: Florian Daude <floriandaude@hotmail.fr>
Requires-Dist: cyclopts>=4.16.1
Requires-Dist: myrtille>=0.1.3
Requires-Dist: pydantic>=2.13.4
Requires-Dist: slcfg>=0.3.4
Requires-Python: >=3.14
Description-Content-Type: text/markdown

# Titto

Titto deletes tenant-scoped rows from a MySQL schema by reading table metadata,
following foreign-key paths back to a tenant table, nulling optional references,
and deleting rows in dependency order.

## Warnings

This is a destructive database tool. Run it against production only after testing
the exact command against a restored copy or staging database.

`--commit` permanently commits the deletes. Without `--commit`, Titto still runs
the `UPDATE` and `DELETE` statements inside a transaction, then rolls them back.
That dry run can still take locks, fire triggers, and affect non-transactional
tables.

Take a backup before any committed run. Prefer a database snapshot or logical dump
that you have already tested restoring. Keep the backup until the tenant removal
has been verified.

## Usage

Show commands:

```sh
uv run titto --help
```

Dry-run a single-column tenant key and cache table metadata as JSON:

```sh
uv run titto delete "$DB_URL" tenant 42 --cache .tables.json
```

Commit the delete:

```sh
uv run titto delete "$DB_URL" tenant 42 --cache .tables.json --commit
```

Use a composite tenant key by passing each primary-key part as its own token:

```sh
uv run titto delete "$DB_URL" tenant 42 eu --cache .tables.json
```

Require selected tables to be empty for the tenant before deleting anything:

```sh
uv run titto delete "$DB_URL" tenant 42 --skip invoice --skip payment
```

Clean up unreferenced rows in tables that are not directly tenant-scoped:

```sh
uv run titto cleanup "$DB_URL" tenant --cache .tables.json
```

The automatic cleanup selection also skips tables with a `UNIQUE` constraint.

Clean up unreferenced rows in explicitly selected tables:

```sh
uv run titto cleanup-tables "$DB_URL" tag category --cache .tables.json
```

Find rows whose outgoing foreign keys resolve to different tenants:

```sh
uv run titto check-cross-tenant "$DB_URL" tenant --limit 100 --cache .tables.json
```

For each table, Titto finds outgoing foreign keys that point to tenant-scoped
tables, then compares the first candidate with each later candidate. Each
referenced table is resolved through its shortest foreign-key path to the tenant
table. The limit is applied per table. By default, results are human-readable
and progress logs are hidden. Use `--json` to print one JSON object per mismatch,
and `--verbose` to show progress logs on stderr.

`DB_URL` is a MySQL URL such as:

```sh
mysql://user:password@localhost:3306/app_schema
```

Avoid putting real passwords directly in shell history. Use an environment
variable, secret manager, or another shell-safe mechanism appropriate for your
environment.

## Cache Notes

`--cache` stores parsed table metadata as JSON. The recommended local cache path
is `.tables.json`; it is ignored by Git.

The cache is tied to the host, port, schema name, and cache format version. Titto
rejects mismatched cache metadata and reparses the database schema. If the schema
changes on the same host and schema name, delete `.tables.json` so Titto can
refresh it.

## Development

Run the test suite:

```sh
uv run python -m unittest
```
