Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 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
b3329b8
ci: new release workflow
AmaseCocoa Feb 17, 2026
b93f8f8
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 17, 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"
}
19 changes: 19 additions & 0 deletions .github/workflows/deploy-rc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: RC Deployment

on:
issue_comment:
types: [created]

jobs:
call-rc-deploy:
if: |
github.event.issue.pull_request &&
contains(github.event.comment.body, '/deploy')
uses: fedi-libs/release-manager/.github/workflows/reusable-rc-deploy.yml@main
with:
comment_user: ${{ github.event.comment.user.login }}
pr_number: ${{ github.event.issue.number }}
package_name: ${{ github.event.repository.name }}
secrets:
app_id: ${{ secrets.RELEASE_BOT_APP_ID }}
private_key: ${{ secrets.RELEASE_BOT_PRIVATE_KEY }}
10 changes: 2 additions & 8 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
on:
push:
branches:
- stable
workflow_call:
workflow_dispatch:

permissions:
actions: write
checks: write
contents: write
deployments: write
id-token: write
pages: write

jobs:
deploy:
Expand All @@ -22,5 +15,6 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: 3.12
cache: 'pip'
- run: pip install mkdocs>=1.6.1 mkdocs-material>=9.6.19
- run: mkdocs gh-deploy --force
73 changes: 17 additions & 56 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -1,60 +1,21 @@
name: Publish Release
name: Formal Release

on:
release:
types: [created]
pull_request:
types: [closed]

jobs:
merge:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Git
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"

- name: Merge main into stable
run: |
git checkout origin/stable
git merge -m "build: merge latest main branch" origin/main
git push origin HEAD:stable
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

pypi-publish:
name: Upload release to PyPI
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/apsig
permissions:
id-token: write
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup uv
uses: astral-sh/setup-uv@v6
with:
version: ">=0.7.21,<0.8"

- name: Set SETUPTOOLS_SCM_PRETEND_VERSION
run: echo "SETUPTOOLS_SCM_PRETEND_VERSION=${GITHUB_REF##*/}" >> $GITHUB_ENV

- name: Display SETUPTOOLS_SCM_PRETEND_VERSION
run: echo "SETUPTOOLS_SCM_PRETEND_VERSION is $SETUPTOOLS_SCM_PRETEND_VERSION"

- name: Build Package
run: |
uv sync --locked
uv build

- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
call-formal-release:
if: |
github.event.pull_request.merged == true &&
startsWith(github.event.pull_request.head.ref, 'release/') &&
github.event.pull_request.user.login == 'fedi-libs-release-bot[bot]'
uses: fedi-libs/release-manager/.github/workflows/reusable-formal-release.yml@main
with:
head_ref: ${{ github.event.pull_request.head.ref }}
pr_body: ${{ github.event.pull_request.body }}
secrets:
app_id: ${{ secrets.RELEASE_BOT_APP_ID }}
private_key: ${{ secrets.RELEASE_BOT_PRIVATE_KEY }}
deploy-docs:
uses: ./.github/workflows/docs.yml
30 changes: 13 additions & 17 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
name: Run release action
name: Release Preparation

on:
push:
branches:
- stable
release:
types: [created]

jobs:
autorelease:
runs-on: ubuntu-latest
steps:
- uses: actions/create-github-app-token@v2
id: app-token
with:
app-id: ${{ vars.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
- uses: googleapis/release-please-action@v4
with:
token: ${{ steps.app-token.outputs.token }}
config-file: release-please-config.json
target-branch: ${{ github.ref_name }}
call-prep:
if: github.event.release.draft == true
uses: fedi-libs/release-manager/.github/workflows/reusable-release-prep.yml@main
with:
tag_name: ${{ github.event.release.tag_name }}
base_branch: stable
secrets:
app_id: ${{ secrets.RELEASE_BOT_APP_ID }}
private_key: ${{ secrets.RELEASE_BOT_PRIVATE_KEY }}
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/
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
4 changes: 4 additions & 0 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 Down
Loading