Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
bb34145
feat: new unified client
AmaseCocoa Feb 9, 2026
badc21e
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 9, 2026
f892e15
fix: fix recursion
AmaseCocoa Feb 9, 2026
2f1eb5f
feat: new client
AmaseCocoa Feb 9, 2026
22eaab6
Merge branch 'feat/new-client' of https://github.com/fedi-libs/apkit …
AmaseCocoa Feb 9, 2026
72794ad
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 9, 2026
185d323
refactor(client): optimize performance
AmaseCocoa Feb 9, 2026
d98a042
Merge branch 'feat/new-client' of https://github.com/fedi-libs/apkit …
AmaseCocoa Feb 9, 2026
6a83e85
Update .gitignore
AmaseCocoa Feb 9, 2026
ec7834a
fix(server): update imports
AmaseCocoa Feb 9, 2026
5100ab2
Create AGENTS.md
AmaseCocoa Feb 9, 2026
400fc80
Update FEDERATION.md
AmaseCocoa Feb 9, 2026
d78b9f0
feat(webfinger): optimize and remove dataclass
AmaseCocoa Feb 9, 2026
ed5df4d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 9, 2026
8cc7705
fix(webfinger): remove type_map from arguments
AmaseCocoa Feb 10, 2026
f8c82d5
Merge branch 'feat/new-client' of https://github.com/fedi-libs/apkit …
AmaseCocoa Feb 10, 2026
c6e3c4c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 10, 2026
9d0f5db
fix(client): fix test failue
AmaseCocoa Feb 10, 2026
a35fe00
Merge branch 'feat/new-client' of https://github.com/fedi-libs/apkit …
AmaseCocoa Feb 10, 2026
fb270aa
feat(webfinger): support to parse and export as xml
AmaseCocoa Feb 10, 2026
af25aa0
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 10, 2026
b1c6821
feat(helper): add host-meta parser/serializer
AmaseCocoa Feb 10, 2026
14cb6a1
Merge branch 'feat/new-client' of https://github.com/fedi-libs/apkit …
AmaseCocoa Feb 10, 2026
025b08a
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 10, 2026
a6071fd
Merge branch 'main' into feat/new-client
AmaseCocoa Feb 18, 2026
356c9c0
Merge branch 'feat/new-client' of https://github.com/fedi-libs/apkit …
AmaseCocoa Feb 18, 2026
99f7620
test: initialize client test
AmaseCocoa Feb 23, 2026
78db9c7
test: create client test
AmaseCocoa Feb 23, 2026
8049455
merge main
AmaseCocoa Feb 23, 2026
ec38256
chore
AmaseCocoa Feb 23, 2026
d0f42cd
fix: add type hint to filtered_set
AmaseCocoa Feb 23, 2026
5335710
chore: auto fixes by prek
AmaseCocoa Feb 23, 2026
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
3 changes: 3 additions & 0 deletions .gemini/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"contextFileName": "AGENTS.md"
}
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -208,4 +208,5 @@ __marimo__/

private_key*.pem

devel/
devel/
data/
6 changes: 0 additions & 6 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,3 @@ repos:
language: system
types_or: [python, pyi]
require_serial: true

- id: pyrefly-check
name: Pyrefly (type checking)
entry: pyrefly check
language: system
pass_filenames: false
194 changes: 194 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
# Agent Guidelines for apkit

This file provides guidelines for AI agents working on the apkit codebase.

## Project Overview

apkit is a modern, fast toolkit for building ActivityPub-based applications with Python. It uses FastAPI for the server, supports async HTTP clients, and handles ActivityPub models, HTTP signatures, and Fediverse protocols.

## Build, Test, and Lint Commands

### Package Manager
This project uses `uv` as the package manager.

### Running Tests
```bash
# Run all tests
uv run pytest

# Run tests with coverage
uv run pytest --cov=src/apkit

# Run a single test
uv run pytest tests/path/to/test_file.py::test_function_name

# Run tests for a specific module
uv run pytest tests/client/
```

### Linting and Formatting
```bash
# Check all files with ruff
uv run ruff check .

# Check and auto-fix issues
uv run ruff check --fix .

# Format all files
uv run ruff format .

# Type checking with pyrefly
uv run pyrefly check .
```

### Pre-commit Hooks
```bash
# Install pre-commit hooks
pre-commit install

# Run all hooks manually
pre-commit run --all-files
```

## Code Style Guidelines

### Imports
1. **Standard library** imports first (e.g., `import json`, `from typing import ...`)
2. **Third-party** imports second (e.g., `import apmodel`, `from fastapi import ...`)
3. **Local/apkit** imports last (e.g., `from ..types import ActorKey`)
4. Use **absolute imports** for external dependencies, **relative imports** for internal modules
5. Sort imports with `collections.abc` before `typing`

Example:
```python
import json
import re
from collections.abc import Iterable, Mapping
from typing import Any, Dict, List, Optional, TypeVar

import aiohttp
import httpx
from apmodel.types import ActivityPubModel

from ..types import ActorKey
from .models import Resource
```

### Formatting
- **Line length**: 88 characters (Black-compatible)
- **Indent**: 4 spaces
- **Quotes**: Double quotes for strings
- Follow **ruff** configuration in `pyproject.toml`

### Type Hints
- **Always use type hints** for function parameters and return types
- Use `from typing import ...` imports at the top
- Use `ParamSpec` and `TypeVar` for generic types
- For Python 3.10+, use `X | Y` syntax instead of `Optional` or `Union` where appropriate

Example:
```python
from typing import Optional, TypeVar

T = TypeVar("T")

def fetch(url: str, headers: Optional[dict] = None) -> dict | None:
...
```

### Naming Conventions
- **Classes**: `PascalCase` (e.g., `ActivityPubClient`, `WebfingerResult`)
- **Functions/Methods**: `snake_case` (e.g., `fetch_actor`, `build_webfinger_url`)
- **Constants**: `SCREAMING_SNAKE_CASE`
- **Private methods/vars**: Prefix with underscore (e.g., `__fetch_actor`, `_client`)
- **Type variables**: Single uppercase letter (e.g., `T`, `P`, `R`)

### Data Classes
- Use `@dataclass(frozen=True)` for immutable models
- Use regular `@dataclass` for mutable response wrappers
- Document classes and methods with docstrings

Example:
```python
@dataclass(frozen=True)
class Link:
"""Represents a link in a WebFinger response."""
rel: str
type: str | None
href: str | None
```

### Error Handling
- Use **specific exceptions** (e.g., `ValueError`, `TypeError`)
- Raise with descriptive messages
- Use custom exceptions in `exceptions.py` for domain-specific errors
- Use `match` statements for pattern matching (Python 3.11+)

Example:
```python
match headers:
case Mapping() as m:
items = m.items()
case None:
items = []
case _:
raise TypeError(f"Unsupported header type: {type(headers)}")
```

### Testing
- Use **pytest** for testing
- Write **descriptive test names** (e.g., `test_build_webfinger_url`)
- Use pytest classes for grouping related tests (e.g., `class TestResource:`)
- Mock external dependencies when appropriate

### Project Structure
```
src/apkit/
├── __init__.py # Package exports
├── _version.py # Version info (auto-generated)
├── abc/ # Abstract base classes
├── cache.py # Caching utilities
├── client/ # HTTP client implementation
│ ├── __init__.py
│ ├── base/ # Base context managers
│ ├── client.py # Main ActivityPubClient
│ ├── exceptions.py # Client exceptions
│ ├── models.py # Data models
│ └── types.py # Type definitions
├── config.py # Configuration
├── helper/ # Helper utilities
├── kv/ # Key-value store implementations
├── models/ # ActivityPub model exports
├── nodeinfo/ # NodeInfo implementation
├── server/ # FastAPI server components
│ ├── app.py # ActivityPubServer
│ ├── routes/ # Route handlers
│ ├── responses.py # Response classes
│ └── types.py # Server types
└── types.py # Common types
```

## Important Notes

- Python **3.11+** is required
- **Type hints are mandatory** for all new code
- Follow the **KISS principle** - Keep It Simple, Stupid
- **Conventional Commits** for commit messages (e.g., `feat:`, `fix:`, `docs:`)
- The codebase is **not stable** - API changes may break backward compatibility

## Dependencies

Key external dependencies:
- `apmodel>=0.5.1` - ActivityPub models
- `apsig>=0.6.0` - HTTP signatures
- `fastapi>=0.116.1` - Web framework (optional, server extra)
- `aiohttp>=3.13.3` - Async HTTP client
- `httpx>=0.28.1` - Sync HTTP client

## Before Submitting

1. Run `uv run ruff check --fix .` to auto-fix linting issues
2. Run `uv run ruff format .` to format code
3. Run `uv run pyrefly check .` to verify type hints
4. Run `uv run pytest` to ensure all tests pass
5. Ensure imports are organized correctly
3 changes: 2 additions & 1 deletion FEDERATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

- [ActivityPub](https://www.w3.org/TR/activitypub/) (Server-to-Server)
- [WebFinger](https://webfinger.net/)
- [Http Signatures](https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures)
- [draft-cavage-http-signatures-12](https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures)
- [Linked Data Signatures 1.0](https://web.archive.org/web/20170923124140/https://w3c-dvcg.github.io/ld-signatures/)
- [NodeInfo](https://nodeinfo.diaspora.software/)
- [RFC 9421: HTTP Message Signatures](https://datatracker.ietf.org/doc/html/rfc9421)

## Supported FEPs

Expand Down
5 changes: 2 additions & 3 deletions examples/send_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,9 @@ async def send_note(recepient: str) -> None:
cc=["https://www.w3.org/ns/activitystreams#Public"],
tag=[
Mention(
href=target_actor.url,
name=f"@{target_actor.preferred_username}"
href=target_actor.url, name=f"@{target_actor.preferred_username}"
)
]
],
)

# Create activity
Expand Down
20 changes: 16 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ server = [
"fastapi>=0.116.1",
"uvicorn>=0.35.0",
]
speed = [
"google-re2>=1.1.20251105",
"lxml>=6.0.2"
]

[tool.uv]
default-groups = "all"
Expand All @@ -51,13 +55,16 @@ version-file = "src/apkit/_version.py"

[dependency-groups]
dev = [
"aioresponses>=0.7.8",
"coverage>=7.10.7",
"pytest>=8.4.1",
"pytest-asyncio>=1.3.0",
"pytest-cov>=7.0.0",
"respx>=0.22.0",
"pyrefly>=0.46.0",
"ruff>=0.14.10",
"prek>=0.3.3"
"prek>=0.3.3",
"pre-commit>=4.5.1",
]
docs = [
"mkdocs>=1.6.1",
Expand Down Expand Up @@ -113,12 +120,17 @@ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"

[tool.pyrefly]
project-includes = [
"src/**/*.py*",
"tests/**/*.py*",
"src/**/*.py",
"tests/**/*.py",
]
project-excludes = [
"scripts/**/*.py"
"scripts/**/*.py",
"**/.*",
"**/*venv/**",
]
search-path = ["src"]
ignore-errors-in-generated-code = true


[tool.ruff.format]
quote-style = "double"
Expand Down
4 changes: 2 additions & 2 deletions src/apkit/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
commit_id: COMMIT_ID
__commit_id__: COMMIT_ID

__version__ = version = '0.3.8.post1.dev10+g892881c3c'
__version_tuple__ = version_tuple = (0, 3, 8, 'post1', 'dev10', 'g892881c3c')
__version__ = version = '0.3.8.post1.dev52+gd0f42cdc1'
__version_tuple__ = version_tuple = (0, 3, 8, 'post1', 'dev52', 'gd0f42cdc1')

__commit_id__ = commit_id = None
12 changes: 9 additions & 3 deletions src/apkit/client/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
from .models import WebfingerResult
from .models import Resource as WebfingerResource
from .client import ActivityPubClient
from .models import Link as WebfingerLink
from .models import Resource as WebfingerResource
from .models import WebfingerResult

__all__ = ["WebfingerResult", "WebfingerResource", "WebfingerLink"]
__all__ = [
"ActivityPubClient",
"WebfingerResult",
"WebfingerResource",
"WebfingerLink",
]
Loading