Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: CI

on:
pull_request:
push:
branches: [main]

permissions:
contents: read

jobs:
test:
name: Python ${{ matrix.python-version }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]

steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: pip

- name: Install package and test dependencies
run: python -m pip install -e ".[dev]"

- name: Run offline tests
run: pytest
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Changelog

All notable changes to this project will be documented here.

This project follows the spirit of [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and uses semantic versioning once releases are published.

## [Unreleased]

### Added

- Public contributor documentation.
- Public security policy.
- Public repository readiness checklist.
- GitHub Actions CI for the offline test suite.

## [0.1.0] - 2026-04-18

### Added

- Initial synchronous Python client for the BriefKlick v2 API.
- Typed models for recipients, uploaded documents, orders, previews, and status responses.
- `preview_letter()` helper for free `/create` + `/preview` workflows.
- `send_letter()` helper for one-shot `/create` + `/send` workflows.
- Offline unit tests using mocked HTTP calls.
- LLM-facing `briefklick-send-letter` skill.
50 changes: 50 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Contributing

Thanks for helping improve `briefklick-python`.

## Development setup

```bash
git clone https://github.com/randomsnowflake/briefklick-python.git
cd briefklick-python
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
pytest
```

The test suite is fully offline. Do not use a real BriefKlick API key in tests.

## Safety model

BriefKlick has no sandbox for `send`.

- `/create` + `/preview` is free and safe.
- `/send` is real mail and chargeable.
- Never add tests, examples, or CI jobs that call the live API.
- Never commit `.env`, API keys, generated letters containing private data, or real recipient details.

## Pull request checklist

Before opening a PR:

- [ ] `pytest` passes locally.
- [ ] New behavior has offline tests using mocked HTTP responses.
- [ ] Public docs are updated when user-facing behavior changes.
- [ ] No secrets or personal data are included in files or fixtures.
- [ ] Send-related code documents ambiguous retry behavior. Blindly retrying `send` can duplicate physical mail.

## Style

- Keep the client small and synchronous.
- Prefer explicit dataclasses/enums over loosely typed dictionaries for public API results.
- Preserve backwards compatibility where practical. If a breaking change is necessary, document it in `CHANGELOG.md`.
- Keep examples copy-pasteable, but use fictional recipients and placeholder filenames.

## Releasing

1. Update the version in `pyproject.toml` and `src/briefklick/__init__.py`.
2. Update `CHANGELOG.md`.
3. Run `pytest`.
4. Build with `python -m build` from a clean checkout.
5. Publish only from a trusted machine after reviewing the source distribution and wheel contents.
29 changes: 25 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# briefklick-python

[![CI](https://github.com/randomsnowflake/briefklick-python/actions/workflows/ci.yml/badge.svg)](https://github.com/randomsnowflake/briefklick-python/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

Idiomatic Python client for the [BriefKlick v2 API](https://api.briefklick.de/v2/swagger.json).
BriefKlick lets you send physical letters in Germany (and a few more countries)
from your code — you upload a PDF, they print, enclose, stamp, and drop it in
Expand All @@ -22,7 +25,7 @@ the post.
- `preview_letter(...)` zero-risk helper (free, no balance needed, returns
the exact rendered PDF)
- `send_letter(...)` one-shot helper that chains `/create` + `/send`
- 45 unit tests, no live API calls required
- 48 unit tests, no live API calls required
- PEP 561 marker included for type checkers (`py.typed`)

## Installation
Expand All @@ -35,6 +38,16 @@ pip install -e ".[dev]" # during development
# pip install briefklick
```

For one-off agent/Hermes usage without mutating the system Python:

```bash
# after publication
uv run --with briefklick python your_script.py

# from a local source checkout before publication
uv run --with /path/to/briefklick-python python your_script.py
```

Supported Python: 3.9+. Only runtime deps are `requests` and `python-dotenv`.

## Configuration
Expand Down Expand Up @@ -160,7 +173,7 @@ except ServerError:
## Testing

```bash
.venv/bin/pytest # 45 tests, 89% coverage, ~0.2s
.venv/bin/pytest # offline tests, no BriefKlick API key required
.venv/bin/pytest --cov=briefklick
```

Expand All @@ -176,11 +189,15 @@ briefklick-python/
│ ├── models.py # Recipient, Document, Order, Status, enums
│ ├── exceptions.py # exception hierarchy
│ └── __init__.py
├── tests/ # 45 tests, offline
├── docs/ # extended examples and API reference
├── tests/ # offline tests
├── docs/ # usage, API reference, publication checklist
├── skill/ # LLM-facing skill for letter preview/send
├── examples/ # runnable scripts
├── .github/workflows/ci.yml # offline test CI
├── .env.example
├── CONTRIBUTING.md
├── SECURITY.md
├── CHANGELOG.md
├── pyproject.toml
└── README.md
```
Expand All @@ -189,6 +206,10 @@ briefklick-python/

- [`docs/usage.md`](docs/usage.md) — extended examples, error handling recipes
- [`docs/api_reference.md`](docs/api_reference.md) — full public API reference
- [`docs/publication.md`](docs/publication.md) — public repository readiness checklist
- [`CONTRIBUTING.md`](CONTRIBUTING.md) — development setup and PR checklist
- [`SECURITY.md`](SECURITY.md) — vulnerability reporting and API-key handling
- [`CHANGELOG.md`](CHANGELOG.md) — release notes
- [`skill/briefklick-send-letter/SKILL.md`](skill/briefklick-send-letter/SKILL.md) — LLM-facing skill for sending letters
from natural language ("send a letter to Peter Maier in 34533 Berlin…").

Expand Down
33 changes: 33 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Security Policy

## Supported versions

Security fixes are accepted for the current `main` branch. Released package versions will be listed in `CHANGELOG.md` once distribution begins.

## Reporting a vulnerability

Please report security issues privately instead of opening a public issue.

Preferred route:

- Open a private GitHub security advisory for this repository, if available.
- If advisories are not available, contact the maintainer through the GitHub profile listed on the repository.

Include:

- A concise description of the issue.
- A minimal reproduction that does not use real API keys or private letter contents.
- Affected version or commit.
- Any relevant logs with secrets redacted.

## Secret handling

`briefklick-python` authenticates with the `apikey` HTTP header. Treat `BRIEFKLICK_API_KEY` like a password.

- Store it in environment variables or a local `.env` file.
- Never commit `.env` files, live keys, generated private letters, or real recipient data.
- Rotate the BriefKlick API key immediately if it is exposed.

## Operational safety

`send()` and `send_letter()` create real, chargeable physical mail. A timeout or 5xx during a send is ambiguous: the order may or may not have been accepted by BriefKlick. Do not automatically retry send operations without reconciling the account/dashboard first.
56 changes: 56 additions & 0 deletions docs/publication.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Public repository readiness

This checklist documents what must be true before making the repository public.

## Required files

- [x] `README.md` with project purpose, warning about chargeable sends, installation, configuration, quickstart, testing, docs, and license.
- [x] `LICENSE` with MIT terms.
- [x] `.env.example` with placeholders only.
- [x] `.gitignore` excluding `.env`, virtualenvs, build output, caches, and local scratch files.
- [x] `CONTRIBUTING.md` with setup, PR checklist, and no-live-API testing rule.
- [x] `SECURITY.md` with private reporting guidance and API-key handling.
- [x] `CHANGELOG.md` with initial release notes.
- [x] `.github/workflows/ci.yml` running the offline test suite.

## Safety requirements

- [x] Examples use fictional recipients and placeholder document names.
- [x] Tests are offline and use mocked HTTP responses only.
- [x] Docs clearly state that `send()` / `send_letter()` are chargeable and not idempotent.
- [x] Docs recommend preview-first workflows by default.
- [x] No live API key is required for tests or examples.

## Final checks before flipping visibility

Run from a clean checkout:

```bash
git status --short
pytest
python -m build
```

Then audit:

```bash
# Current tree, excluding intentional placeholder docs/tests
git grep -n -I -E '(BEGIN (RSA|OPENSSH|PRIVATE) KEY|Bear[e]r [A-Za-z0-9._-]+|Authori[z]ation:|pass[word]=|sec[ret]=)' -- . || true

# History smoke test for accidentally committed credentials
git log --all -p -- . ':(exclude).env.example' \
| grep -En '(BEGIN (RSA|OPENSSH|PRIVATE) KEY|Bear[e]r [A-Za-z0-9._-]+|Authori[z]ation:|pass[word]=|sec[ret]=)' \
| head -50 || true

# Large blobs that may be generated/private artifacts
git rev-list --objects --all \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
| awk '$1=="blob" && $3>1048576 {print $3, $4}' \
| sort -nr
```

If any finding is a real secret or private document, rotate/revoke the credential and rewrite history before making the repository public. Do not rely on deletion in a later commit.

## Repository visibility

This PR prepares the content for public visibility. The actual GitHub visibility change should be done separately by a maintainer after reviewing the PR and confirming there are no private forks, branch protections, or release automation assumptions that depend on the repository being private.
16 changes: 11 additions & 5 deletions skill/briefklick-send-letter/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,18 @@ Keep the user's exact spelling (umlauts, ß) — the PDF will render it.

### Dependencies

The host project is expected to have `briefklick` installed. If not:
The host project is expected to have `briefklick` installed. If not, avoid
system-wide `pip` on managed Python installs; use a project venv or `uv`.

```bash
pip install briefklick
# or, from the source repo:
pip install -e path/to/briefklick-python
# In a project virtualenv
python -m pip install briefklick

# One-off Hermes/agent execution after package publication
uv run --with briefklick python your_script.py

# From a source checkout before publication
uv run --with /path/to/briefklick-python python your_script.py
```

### Preview only (zero-risk, recommended first step)
Expand Down Expand Up @@ -249,4 +255,4 @@ Skill response:
6. Return `order.id`, `order.price_eur`, and any tracking from
`get_status(order.id)` (polled after a short delay).

See `examples/natural_language_to_code.md` for the full prompt → code mapping.
See `references/natural_language_to_code.md` for the full prompt → code mapping.
Loading