{"id":31534096,"url":"https://github.com/nigini/tinyfedipub","last_synced_at":"2026-04-12T11:48:43.408Z","repository":{"id":314696392,"uuid":"1048228408","full_name":"nigini/tinyFediPub","owner":"nigini","description":"A Fediverse Activity server for your \"tinyHome\" on the web","archived":false,"fork":false,"pushed_at":"2025-09-22T03:32:41.000Z","size":113,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-22T04:24:42.164Z","etag":null,"topics":["activitypub","fediverse","fediverse-server","flask","minimalist","python3"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nigini.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-01T06:08:37.000Z","updated_at":"2025-09-22T03:32:44.000Z","dependencies_parsed_at":"2025-09-14T07:16:01.012Z","dependency_job_id":"2847cf0b-e83b-40cb-9c40-7026e06f09e0","html_url":"https://github.com/nigini/tinyFediPub","commit_stats":null,"previous_names":["nigini/tinyfedipub"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/nigini/tinyFediPub","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nigini%2FtinyFediPub","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nigini%2FtinyFediPub/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nigini%2FtinyFediPub/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nigini%2FtinyFediPub/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nigini","download_url":"https://codeload.github.com/nigini/tinyFediPub/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nigini%2FtinyFediPub/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278266896,"owners_count":25958733,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-04T02:00:05.491Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["activitypub","fediverse","fediverse-server","flask","minimalist","python3"],"created_at":"2025-10-04T05:17:18.990Z","updated_at":"2026-04-12T11:48:43.401Z","avatar_url":"https://github.com/nigini.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tinyFedi\n\nA minimalist ActivityPub server designed for easy integration with personal websites.\n\n## Overview\n\nSimple file-based ActivityPub implementation that serves federated content \nfrom static JSON files. Perfect for personal blogs and small websites \nwanting to join the fediverse without a complex infrastructure.\n\n\u003cdiv style=\"text-align:center\"\u003e\n    \u003cimg src=\"./docs/images/tinyfedi1.png\" \n    style=\"width:70%; min-width:500px; max-width:800px\" \n    alt=\"tinyFedi connects content, like blog posts, to the Fediverse\"\u003e\n\u003c/div\u003e\n\n## Tech Stack\n\n- **Python 3.11+** - Required for modern datetime handling\n- **Flask** - Lightweight web framework\n- **Jinja2** - Template engine for ActivityPub entities\n- **File-based storage** - All content served from static JSON files\n- **Zero dependencies** - Minimal external requirements\n\n**If you want to know more about how I implemented this software, and learn\na lot about ActivityPub in the process, here are the posts (*you can also Follow \nall updates at @blog@nigini.me - which is using this exact software to Federate):***\n\n1. [Building tinyFedi - part 1](https://nigini.me/blog/3-fediverse_server_part1):\n   Here we explore the basics of AP and build around Actors and its Outbox.\n2. [Building tinyFedi - part 2](https://nigini.me/blog/4-fediverse_server_part2):\n   We finish the basics by building around the Inbox and Activity delivery.\n3. Building tinyFedi - part 3: *coming soon* HTTP Signatures\n4. Building tinyFedi - part 4: *coming soon* Update, Like, and Annouce\n   Activities.\n\n## Setup\n\n### 1. Generate Cryptographic Keys\n\nActivityPub requires public/private key pairs for secure federation:\n\n```bash\nmkdir keys\nopenssl genrsa -out keys/private_key.pem 2048\nopenssl rsa -in keys/private_key.pem -pubout -out keys/public_key.pem\n```\n\n**Security Note:** Keys are automatically excluded from version control via `.gitignore`.\n\n### 2. Configuration\n\nCopy the example configuration file and customize it for your setup:\n\n```bash\ncp config.json.example config.json\n```\n\n**!!!** Actor's profile auto-generates from config on startup\n\n### 3. Take it for a Ride\n\n```bash\npython app.py\n```\n\nAdd posts using the CLI:\n\n```bash\n./client/new_post.py --title \"Post Title\" --content \"Content\" --url \"https://yourblog.com/post\"\n```\n**Note:** New posts are automatically delivered to followers when created.\n\nEdit existing posts:\n\n```bash\n./client/edit_post.py --post-id \"550e8400-e29b-41d4-a716-446655440000\"\n```\n**Note:** Updated posts are automatically delivered to followers when edited.\n\nProcess incoming activities: \n\n```bash\npython -m activity_processor` #or set up as a cron job\n```\n**Note:** Activities received in the inbox are automatically queued to be \nprocessed! \n\n\n## Deployment\n\nDesigned to run behind a reverse proxy alongside existing websites:\n\n```nginx\nlocation /activitypub/ {\n    proxy_pass http://localhost:5000/activitypub/;\n}\n```\n\n## Development\n\n### Running Tests\n\nInstall dependencies and run the test suite:\n\n```bash\npip install -r requirements.txt\npython -m pytest tests/ -v\n```\n\n### Writing Tests\n\nThis project uses a comprehensive test isolation strategy to ensure reliable testing. All test classes should inherit from `TestConfigMixin` for proper test isolation.\n\n**Key principles:**\n- Each test gets its own temporary directory and configuration\n- Module reload prevents global variable caching issues\n- Configuration-driven paths (no hardcoded references)\n- Import app modules INSIDE test methods, AFTER `setUp()` runs\n\n**See `tests/test_config.py`** for complete documentation, usage patterns, helper methods, and implementation details of the test configuration strategy.\n\n## Template System\n\nActivityPub entities are generated using Jinja2 templates for maintainability and extensibility:\n\n```\ntemplates/\n├── base/             # Shared base templates\n│   ├── activity.json.j2   # Base for all activity types\n│   └── post.json.j2       # Base for Article/Note\n├── objects/          # ActivityStreams Object types\n│   ├── actor.json.j2      # Person/Service actors\n│   ├── article.json.j2    # Blog posts, articles\n│   └── note.json.j2       # Short messages\n├── activities/       # ActivityStreams Activity types\n│   ├── create.json.j2     # Create activities\n│   ├── update.json.j2     # Update activities\n│   └── accept.json.j2     # Accept activities (follow responses)\n└── collections/      # ActivityStreams Collections\n    ├── outbox.json.j2     # Outbox collection (paginated)\n    └── followers.json.j2  # Followers collections\n```\n\n**Design Philosophy:**\n- **Type-specific templates** - Each ActivityStreams type has its own template\n- **Extensible** - Easy to add new object types (Note, Image, Event) and activity types (Like, Follow, Announce)\n- **Spec-compliant** - Templates ensure proper ActivityPub/ActivityStreams structure\n- **Configurable** - All values injected from `config.json` and runtime data\n\n\n## Federation Features\n\n**Implemented:**\n- **WebFinger Discovery** - `.well-known/webfinger` for actor discovery\n- **Actor Profile** - Dynamic actor generation from config\n- **Outbox Collection** - Dynamically serves published activities with pagination\n- **Individual Endpoints** - Posts and activities accessible via direct URLs\n- **Inbox Endpoint** - Receives activities from other federated servers with HTTP signature verification\n- **Followers Collection** - Manages and serves follower list\n- **Content Negotiation** - Proper ActivityPub headers and validation\n- **HTTP Signature Verification** - Cryptographic validation of incoming activities (configurable)\n- **HTTP Signature Signing** - Sign outgoing activities for secure delivery\n- **Likes Collection** - Per-post likes tracking with collection endpoint at `/posts/{id}/likes`\n- **Shares Collection** - Per-post shares tracking with collection endpoint at `/posts/{id}/shares`\n- **Inbound Create** - Receive and store posts from trusted actors in `posts/remote/`, with pristine object storage and provenance metadata\n- **Trust Module** - Policy-based acceptance of incoming Create activities (block list, following, addressed to us, reply to known post, trusted signer)\n- **Inbox Provenance** - HTTP signature identity (`signed_by`) stored alongside inbox activities for trust evaluation\n- **C2S Bearer Token Auth** - Token-based authentication for client-to-server endpoints\n- **C2S Outbox POST** - Clients submit AS2 objects, server wraps in Create activity and delivers\n- **Streams/Posts** - Object-centric paginated collection of posts (not activities) with inline reaction summaries\n- **Actor Streams Discovery** - Actor profile includes `streams` array for client discovery\n\n**File Structure:**\n```\ndata/\n├── actor.json           # Your actor profile (auto-generated)\n├── followers.json       # Collection of followers (auto-generated)\n├── blocked.json         # Block list (actors and domains)\n├── posts/\n│   ├── local/           # Your authored post objects (UUID directories)\n│   │   └── {uuid}/\n│   │       ├── post.json       # Post object with inline reaction summaries\n│   │       ├── likes.json      # OrderedCollection of actors who liked\n│   │       ├── shares.json     # OrderedCollection of actors who shared\n│   │       └── replies.json    # OrderedCollection of replies\n│   └── remote/          # Received posts from followed actors (URL-derived paths)\n│       └── {domain}/{path}/\n│           ├── object.json     # Original AS2 object (untouched)\n│           └── metadata.json   # Provenance: signed_by, received_at, accepted_by_rule\n├── outbox/              # Outgoing activity objects\n│   └── create-20250921-143022-123456.json\n└── inbox/               # Received activities from other servers\n    ├── follow-*.json        # Activity files (original, untouched)\n    ├── follow-*.meta.json   # Provenance metadata (signed_by, received_at)\n    └── queue/               # Symlinks to activities awaiting processing\n```\n\n**Current Capabilities:**\n- ✅ Others can discover your actor via WebFinger\n- ✅ Others can follow your actor and read your posts\n- ✅ You receive and process all incoming activities\n- ✅ Automatic follower management (add/remove followers)\n- ✅ Auto-respond to Follow requests with Accept activities\n- ✅ Deliver new posts to all followers automatically\n- ✅ HTTP signature verification for incoming activities (configurable)\n- ✅ HTTP signature signing for all outgoing deliveries\n- ✅ Receive and track Like activities per post\n- ✅ Receive and track Announce (share) activities per post\n- ✅ Receive and store Create activities from trusted actors\n- ✅ Policy-based trust evaluation for incoming content (see `docs/ACCEPT_POST_POLICY.md`)\n\n**Configuration Options:**\n- `auto_accept_follow_requests` - Automatically accept follow requests (default: true). Set to `false` for manual approval of followers\n- `require_http_signatures` - Require HTTP signatures on all incoming activities (default: false). Set to `true` for production to reject unsigned server-to-server traffic\n- `max_page_size` - Maximum items per page for paginated collections like outbox (default: 20). Clients can request smaller pages via `?limit=N`\n\n## What's Next\n\n**Architecture:**\n- **Data access layer** — Centralize file-based data access (followers, following, blocked, posts) into a shared module, replacing scattered direct file I/O across processors and endpoints\n- **Outbox queue processing** — Move outbox delivery into the queue system so CLI tools just create + queue activities, and the processor handles delivery (with retry on failure)\n- **Integrate delivery into processors** — Move `activity_delivery.py` into the `activity_processor` module as `delivery.py`, since delivery is outbox processing\n- **Per-follower delivery tracking** — Expand the queue to track delivery per-follower, enabling independent retries for failed deliveries\n\n**Activity Types:**\n- **Announce outbound** — Send Announce activities to boost posts to followers\n- **Delete** — Tombstoning posts + federated Delete delivery. See [AP §6.11](https://www.w3.org/TR/activitypub/#delete-activity-outbox)\n- **EmojiReact** — Rich reactions per [FEP-c0e0](https://codeberg.org/fediverse/fep/src/branch/main/fep/c0e0/fep-c0e0.md)\n\n**Client-to-Server:**\n- **Following** — Send Follow activities, maintain `following.json`, handle Accept/Reject\n- **Inbox materialization** — `streams/home` with objects from followed actors\n- **Microsyntax processing** — Server-side `@mention` / `#hashtag` / URL resolution on outbox POST\n- **Object Integrity Proofs** — Self-authenticating posts via [FEP-8b32](https://codeberg.org/fediverse/fep/src/branch/main/fep/8b32/fep-8b32.md), embedding cryptographic signatures in objects (like Nostr's `sig`). HIGH PRIORITY: sign all posts from the start\n\n**Other:**\n- Proper logging system (replace `print()` with Python's `logging` module)\n- Manual follow approval workflow\n- Mention and reply handling\n\n## Design Notes\n\nSee `docs/` for detailed design documents:\n- `docs/CLIENT_CONTRACT.md` — What tinyFedi guarantees to clients (normalization, streams, auth)\n- `docs/ACCEPT_POST_POLICY.md` — How tinyFedi decides to accept/reject incoming Create activities (trust rules, forwarding, future trust graph)\n- `docs/AP_Federation/SignaturesFlows.md` — HTTP signature flows\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnigini%2Ftinyfedipub","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnigini%2Ftinyfedipub","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnigini%2Ftinyfedipub/lists"}