Metadata-Version: 2.3
Name: sealedlog
Version: 0.1.0
Summary: Encrypted, append-only, line-oriented JSON log
Author: Louis Maddox
Author-email: Louis Maddox <louismmx@gmail.com>
Requires-Dist: pynacl>=1.5
Requires-Python: >=3.12
Description-Content-Type: text/markdown

# sealedlog

An encrypted, append-only, line-oriented log for JSON records — think SQLite
for encrypted append-only JSON logs, minus the SQL. Small, embeddable, no
daemon, no external services: just a file format and the code to read and
write it correctly.

- Appending a record is a byte-append to the file — no rewriting, no
  reordering. Git diffs and merges cleanly on files built this way.
- Each line decrypts independently. A corrupted or truncated line doesn't
  block reading the lines before or after it.
- Lines are bound to the logical stream they were written for. A line copied
  from one stream into another fails to authenticate, even under the correct
  key.
- The key comes from a passphrase via Argon2id, and a wrong passphrase is
  detected immediately and unambiguously.

See [docs/FORMAT.md](docs/FORMAT.md) for the on-disk format and threat model.

## Usage

```python
from pathlib import Path
from sealedlog import SealedLog, Vault

vault = Vault.create("correct horse battery staple", namespace="myapp")
key = vault.unlock("correct horse battery staple", namespace="myapp")

log = SealedLog(Path("events.jsonl.enc"), key, "orders", namespace="myapp")
log.append({"order_id": 1, "total": 42})

for record in log:
    print(record)
```

`namespace` is your application's own identifier — it, the stream ID, and the
library's format version are all folded into the authenticated data for every
line, so two different applications never accidentally produce
cross-compatible ciphertexts.

## Non-goals

`sealedlog` doesn't know what a "user" or "owner" is, doesn't validate record
schemas, doesn't fold or merge records, doesn't decide where files live, and
doesn't coordinate concurrent writers to the same file. All of that is
application-level policy built on top of a plain sequence of records.

## Development

```sh
uv sync --all-groups
uv run ruff format --check .
uv run ruff check .
uv run ty check
uv run pytest
```
