Metadata-Version: 2.5
Name: s-authkit-contracts
Version: 0.1.0
Summary: Контракты доступа общие для authkit-server/authkit-client/leasekit/sessionkit: форма X-API-Key, скоупы `семейство:*`, TOTP-окно допуска, статус сессии, in-memory тестовый стенд. Только stdlib.
Author: Dmitry
License: MIT
Keywords: api-key,auth,contracts,scopes,stdlib-only,totp
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.11
Description-Content-Type: text/markdown

# s-authkit-contracts — общий язык доступа

Форма `X-API-Key`, скоупы `семейство:*`, окно допуска TOTP-кода, статус
сессии-владения и канонический in-memory стенд для тестов — то, что должно
пониматься ОДИНАКОВО по обе стороны любой границы между authkit-server,
authkit-client, leasekit и sessionkit. **Ноль зависимостей** — только stdlib.

## Зачем этот кит

До него форма `X-API-Key` существовала в трёх несовместимых видах в разных
репозиториях портфеля (authkit-server: `ak_<key_id>_<secret>` + sha256 секрета
с O(1)-поиском по `key_id`; atlas-backend: голый `token_urlsafe(32)` без
префикса, поиск линейным перебором; skillery-backend webhook: HMAC-подпись
тела с отдельным заголовком `X-Skillery-Key-Id`), а `has_scope` и константа
шага TOTP были продублированы независимо внутри одного и того же кита. Этот
кит фиксирует форму authkit-server (боевую, уже параметризованную заголовком
в servicekit) как контракт — единственный, который остальные обязаны понимать
одинаково — без переиздания сервисной логики.

## Чем владеет

- `authkit_contracts.apikey` — `HEADER`, `PREFIX`, `HASH_ALGO`,
  `PresentedKey`, `parse_presented`/`format_presented`, `hash_secret`,
  структурный `ApiKeyRecord` (Protocol).
- `authkit_contracts.scopes` — `has_scope(granted, required)`.
- `authkit_contracts.totp_window` — `STEP_SECONDS`, `DEFAULT_VALID_WINDOW`,
  `valid_counters()`.
- `authkit_contracts.status` — `Status` (`live`/`expired`/`blocked`/
  `logged_out`), `is_usable()`.
- `authkit_contracts.testing` — `InMemoryApiKeyStore`, `issued_key()`.

## Чем НЕ владеет

- **Сервисной логикой ключей** (issue/verify/revoke, реальный store) — она
  остаётся в authkit-server, который знает про `subject`, `expires_at`,
  `label` и конкретную базу данных.
- **HTTP-проводкой** (FastAPI dependency, middleware) — она в servicekit.
- **Генерацией и проверкой TOTP-кода** (pyotp, base32-секрет) — генерация в
  totpkit (чужие сервисы: PyPI/Bitrix/GitHub), проверка в authkit-server
  (свой второй фактор). Здесь только число шага и формула допуска.
- **Здоровьем аккаунта во всей полноте.** `Status` — одна ось (владение
  сессией прямо сейчас), а не слияние `HealthState`/`SsoState`/
  `AccountAuthState`/`AccountState`/`AccessState`/`LeaseVerdict` — они
  отвечают на разные вопросы и намеренно не сведены в один enum.

## Установка

```bash
pip install s-authkit-contracts
```

Импортируется как `authkit_contracts`. Требуется Python 3.11+.

## Быстрый старт

```python
from authkit_contracts import (
    format_presented, parse_presented, hash_secret,
    has_scope, Status, is_usable, InMemoryApiKeyStore, issued_key,
)

# Собрать и разобрать предъявляемую строку.
presented = format_presented("a1b2c3", "секрет")
parsed = parse_presented(presented)          # PresentedKey(key_id='a1b2c3', secret='секрет')
hash_secret(parsed.secret)                    # версионированный sha256

# Скоупы: семейство:* покрывает любое право семейства.
has_scope(["posts:*"], "posts:read")          # True
has_scope(["posts:read"], "posts:write")      # False

# Статус сессии-владения.
is_usable(Status.LIVE)                        # True
is_usable(Status.EXPIRED)                     # False

# Тестовый стенд.
store = InMemoryApiKeyStore()
plaintext = issued_key(store, "totp:read")    # готовая строка для X-API-Key
```

## Разработка

```bash
uv sync
uv run pytest -q
uv run ruff check .
uv run --with import-linter lint-imports
```
