{"id":45999081,"url":"https://github.com/aluitink/broca","last_synced_at":"2026-03-05T06:00:55.933Z","repository":{"id":328687925,"uuid":"1115824373","full_name":"aluitink/Broca","owner":"aluitink","description":".NET ActivityPub Client/Server","archived":false,"fork":false,"pushed_at":"2026-02-28T23:00:32.000Z","size":1619,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-28T23:12:17.969Z","etag":null,"topics":["activitypub","activitypub-client","activitypub-server","activitystreams","fediverse","fediverse-client","fediverse-server"],"latest_commit_sha":null,"homepage":"","language":"C#","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/aluitink.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","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-12-13T16:20:29.000Z","updated_at":"2026-02-28T20:05:35.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/aluitink/Broca","commit_stats":null,"previous_names":["aluitink/broca"],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/aluitink/Broca","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aluitink%2FBroca","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aluitink%2FBroca/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aluitink%2FBroca/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aluitink%2FBroca/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aluitink","download_url":"https://codeload.github.com/aluitink/Broca/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aluitink%2FBroca/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30111779,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-05T03:40:26.266Z","status":"ssl_error","status_checked_at":"2026-03-05T03:39:15.902Z","response_time":93,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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","activitypub-client","activitypub-server","activitystreams","fediverse","fediverse-client","fediverse-server"],"created_at":"2026-02-28T22:02:56.750Z","updated_at":"2026-03-05T06:00:55.897Z","avatar_url":"https://github.com/aluitink.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Broca\n\nA modular .NET library for building ActivityPub-enabled applications.\n\n## What is Broca?\n\nBroca provides a complete, standards-compliant implementation of the ActivityPub protocol for .NET developers. It offers both client and server capabilities with a clean API that makes federation simple.\n\n## Features\n\n### Core ActivityPub Protocol\n\n**Client-to-Server (C2S)**\n- ✅ Post activities to outbox (Create, Like, Follow, Announce, Undo)\n- ✅ Server-assigned activity IDs with proper URL structure\n- ✅ Follow/Unfollow relationship management\n- ✅ HTTP Signature authentication for outbox operations\n- ✅ API key-based client authentication\n\n**Server-to-Server (S2S) Federation**\n- ✅ Cross-server activity delivery with HTTP Signatures\n- ✅ Background delivery queue with retry logic\n- ✅ Follow/Accept/Reject workflow (auto-accept and manual approval modes)\n- ✅ Undo operations (Follow, Like, Announce)\n- ✅ Delete activities with Tombstone support\n- ✅ Update Person for profile changes\n- ✅ Move activity for account migration with `alsoKnownAs` validation\n- ✅ Date validation (reject stale or future-dated requests)\n- ✅ Actor caching and refresh\n\n**Shared Inbox**\n- ✅ Efficient batch delivery to multiple local users\n- ✅ To, Cc, and Bcc addressing support\n- ✅ Public addressing (`https://www.w3.org/ns/activitystreams#Public`)\n- ✅ Followers collection addressing\n- ✅ Smart routing (mixed local/remote recipient handling)\n\n### Collections \u0026 Discovery\n\n- ✅ Followers and Following collections\n- ✅ Custom collections (manual curation and query-based)\n- ✅ Ordered collection pagination\n- ✅ WebFinger support for @user@domain discovery\n- ✅ NodeInfo 2.0 and 2.1 (server metadata and statistics)\n\n### Media \u0026 Attachments\n\n- ✅ Blob storage for media files\n- ✅ Media endpoint (`/users/:username/media/:blobId`)\n- ✅ Attachments in Create activities\n- ✅ Content-Type preservation and validation\n\n### Security \u0026 Authentication\n\n- ✅ HTTP Signatures (draft-cavage-http-signatures-12)\n- ✅ Public key infrastructure with cryptographic key generation\n- ✅ Request date validation (prevents replay attacks)\n- ✅ Outbox authentication (users can only post as themselves)\n- ✅ Signature verification for incoming federation\n\n### Content Type Handling\n\n- ✅ `application/activity+json`\n- ✅ `application/ld+json`\n- ✅ Profile parameter support (`application/ld+json; profile=\"...\"`)\n- ✅ Mastodon compatibility\n\n### Administration\n\n- ✅ Back-channel user management via ActivityPub protocol\n- ✅ System actor for server-level operations\n- ✅ Create/Update/Delete users through admin endpoints\n- ✅ Automatic key pair generation for new actors\n\n### Persistence \u0026 Storage\n\n- ✅ Modular storage abstractions (`IActorRepository`, `IActivityRepository`, `IBlobStorageService`)\n- ✅ In-memory implementation (development/testing)\n- ✅ File-based implementation (production-ready)\n- ✅ Easy integration with custom storage backends\n\n### Developer Experience\n\n- ✅ Comprehensive integration test suite (88+ tests)\n- ✅ Multi-server federation testing infrastructure\n- ✅ Activity builder API for constructing valid ActivityStreams objects\n- ✅ Blazor component library for building federated UIs\n- ✅ Docker and Let's Encrypt support for production deployment\n\n## Standards Compliance\n\nBroca implements the core [ActivityPub W3C Recommendation](https://www.w3.org/TR/activitypub/) with extensions for real-world interoperability:\n\n- ✅ **ActivityPub** - Client-to-Server and Server-to-Server protocols\n- ✅ **ActivityStreams 2.0** - Core vocabulary and extended types\n- ✅ **HTTP Signatures** - Request authentication (draft-cavage-http-signatures-12)\n- ✅ **WebFinger** (RFC 7033) - User discovery via @username@domain\n- ✅ **NodeInfo 2.0/2.1** - Server metadata and statistics\n\n**Interoperability Target:** Mastodon (primary), with support for Threads, Pixelfed, and Pleroma.\n\n**Known Limitations:**\n- `featured` collection not yet exposed on actor documents (pinned posts)\n- Follow/Following collections for locked accounts publicly visible (privacy enhancement pending)\n\nAll critical and high-priority federation features are complete. See [docs/s2s-compliance-todo.md](docs/s2s-compliance-todo.md) for detailed compliance tracking.\n\n## Library Components\n\n- **Broca.ActivityPub.Core** - Shared interfaces and models\n- **Broca.ActivityPub.Client** - Client library for federated requests\n- **Broca.ActivityPub.Server** - Server components for hosting actors\n- **Broca.ActivityPub.Persistence** - Storage abstractions (in-memory, file-based)\n- **Broca.ActivityPub.Components** - Blazor components for web UIs\n\n## Quick Start: Using the Libraries\n\n### Install Packages\n\n```bash\n# For client-side federation\ndotnet add package Broca.ActivityPub.Client\n\n# For hosting ActivityPub actors\ndotnet add package Broca.ActivityPub.Server\n```\n\n### Client Example\n\n```csharp\n// Add to DI container\nservices.AddActivityPubClientAuthenticated(\n    actorId: \"https://myserver.com/users/alice\",\n    privateKeyPem: privateKey,\n    publicKeyId: \"https://myserver.com/users/alice#main-key\"\n);\n\n// Use the client\nvar client = serviceProvider.GetRequiredService\u003cIActivityPubClient\u003e();\n\n// Create and post a note\nvar note = client.CreateActivityBuilder()\n    .CreateNote(\"Hello, fediverse!\")\n    .ToPublic()\n    .Build();\n    \nawait client.PostToOutboxAsync(note);\n```\n\n### Server Example\n\n```csharp\nvar builder = WebApplication.CreateBuilder(args);\n\n// Add Broca server with all required services\nbuilder.Services.AddBrocaServer(builder.Configuration);\n\n// Add simple identity provider for basic actor setup\nbuilder.Services.AddSimpleIdentityProvider(builder.Configuration);\n\nvar app = builder.Build();\n\n// Initialize identities on startup\nvar identityService = app.Services.GetService\u003cIdentityProviderService\u003e();\nif (identityService != null)\n{\n    await identityService.InitializeIdentitiesAsync();\n}\n\napp.MapControllers();\napp.Run();\n```\n\n**appsettings.json:**\n```json\n{\n  \"ActivityPub\": {\n    \"BaseUrl\": \"https://myblog.com\",\n    \"PrimaryDomain\": \"myblog.com\"\n  },\n  \"IdentityProvider\": {\n    \"SimpleIdentity\": {\n      \"Username\": \"alice\",\n      \"DisplayName\": \"Alice Smith\",\n      \"Summary\": \"Personal blog about tech and life\"\n    }\n  }\n}\n```\n\nNow your application is followable as `@alice@myblog.com` from Mastodon and other federated platforms!\n\n## Running the Sample Applications\n\nThe `samples/` folder contains two complete applications demonstrating Broca's capabilities:\n\n### Broca.Sample.WebApi\n\nA full-featured ActivityPub server with:\n- WebFinger support for user discovery\n- ActivityPub inbox/outbox endpoints\n- File-based persistence\n- System actor for server-level federation\n\n### Broca.Sample.BlazorApp\n\nA client-side Blazor WebAssembly application for:\n- Looking up ActivityPub users by @handle\n- Browsing user outboxes\n- Testing client features in the browser\n\n### Running with Docker Compose\n\nThe easiest way to run the samples locally:\n\n```bash\ncd samples\ndocker-compose up -d\n```\n\nThis starts both applications:\n- **WebApi**: http://localhost:5050\n- **Blazor App**: http://localhost:5051\n\nTest WebFinger:\n```bash\ncurl \"http://localhost:5050/.well-known/webfinger?resource=acct:user@localhost\"\n```\n\nTest actor endpoint:\n```bash\ncurl http://localhost:5050/ap/users/user\n```\n\n### Running without Docker\n\n```bash\n# Run the WebApi\ncd samples/Broca.Sample.WebApi\ndotnet run\n\n# In another terminal, run the Blazor app\ncd samples/Broca.Sample.BlazorApp\ndotnet run\n```\n\n## Production Deployment with Let's Encrypt\n\nActivityPub **requires HTTPS** in production for federation. Here's how to deploy with automatic Let's Encrypt certificates.\n\n### Prerequisites\n\n- A domain name (e.g., `example.com`)\n- A server with Docker installed\n- DNS A record pointing your domain to your server's IP\n\n### Step 1: Set Up nginx-proxy with Let's Encrypt\n\nThis creates a reverse proxy that automatically handles HTTPS certificates:\n\n```bash\n# Create the proxy network\ndocker network create nginx-proxy\n\n# Start nginx-proxy\ndocker run -d -p 80:80 -p 443:443 \\\n  --name nginx-proxy \\\n  --network nginx-proxy \\\n  --restart always \\\n  -v /var/run/docker.sock:/tmp/docker.sock:ro \\\n  -v nginx-certs:/etc/nginx/certs \\\n  -v nginx-vhost:/etc/nginx/vhost.d \\\n  -v nginx-html:/usr/share/nginx/html \\\n  nginxproxy/nginx-proxy\n\n# Start Let's Encrypt companion\ndocker run -d \\\n  --name nginx-proxy-acme \\\n  --network nginx-proxy \\\n  --restart always \\\n  --volumes-from nginx-proxy \\\n  -v /var/run/docker.sock:/var/run/docker.sock:ro \\\n  -v acme-state:/etc/acme.sh \\\n  -e DEFAULT_EMAIL=your-email@example.com \\\n  nginxproxy/acme-companion\n```\n\n### Step 2: Configure Broca for Production\n\nIn the `samples/` directory, create or edit `docker-compose.override.yml`:\n\n```yaml\nservices:\n  broca-api:\n    environment:\n      - ASPNETCORE_ENVIRONMENT=Production\n      - ActivityPub__BaseUrl=https://example.com\n      - ActivityPub__PrimaryDomain=example.com\n      - VIRTUAL_HOST=example.com\n      - VIRTUAL_PORT=8080\n      - LETSENCRYPT_HOST=example.com\n      - LETSENCRYPT_EMAIL=your-email@example.com\n    networks:\n      - broca-network\n      - nginx-proxy\n\n  broca-blazor:\n    environment:\n      - VIRTUAL_HOST=app.example.com\n      - VIRTUAL_PORT=80\n      - LETSENCRYPT_HOST=app.example.com\n      - LETSENCRYPT_EMAIL=your-email@example.com\n    networks:\n      - broca-network\n      - nginx-proxy\n\nnetworks:\n  nginx-proxy:\n    external: true\n```\n\n### Step 3: Deploy\n\n```bash\ncd samples\ndocker-compose up -d\n```\n\nThe acme-companion will automatically:\n1. Request Let's Encrypt certificates for your domains\n2. Configure nginx to serve your applications over HTTPS\n3. Renew certificates before they expire\n\nYour server will be accessible at:\n- **API**: https://example.com\n- **Blazor App**: https://app.example.com\n\nUsers can now follow actors like `@user@example.com` from Mastodon, Misskey, and other ActivityPub platforms!\n\n### Verification\n\nTest your production deployment:\n\n```bash\n# Test WebFinger\ncurl \"https://example.com/.well-known/webfinger?resource=acct:user@example.com\"\n\n# Test actor endpoint\ncurl https://example.com/ap/users/user\n\n# Verify HTTPS certificate\ncurl -vI https://example.com 2\u003e\u00261 | grep \"SSL certificate verify ok\"\n```\n\n### Troubleshooting\n\n**Certificate not generated?**\n- Ensure DNS is properly configured and propagated\n- Check logs: `docker logs nginx-proxy-acme`\n- Verify port 80 is accessible (required for Let's Encrypt validation)\n\n**Can't federate with Mastodon?**\n- Ensure `ActivityPub__BaseUrl` uses HTTPS\n- Verify your server is accessible from the internet\n- Check HTTP Signature implementation if requests are rejected\n\n## Custom Identity Providers\n\nThe SimpleIdentityProvider is great for single-user scenarios. For multi-user applications, implement `IIdentityProvider`:\n\n```csharp\npublic class CustomIdentityProvider : IIdentityProvider\n{\n    public Task\u003cActorIdentity?\u003e GetIdentityByUsernameAsync(string username)\n    {\n        // Load user from your database\n        var user = await _userRepository.GetByUsernameAsync(username);\n        \n        return new ActorIdentity\n        {\n            Username = user.Username,\n            DisplayName = user.DisplayName,\n            Summary = user.Bio,\n            PublicKeyPem = user.PublicKey,\n            PrivateKeyPem = user.PrivateKey,\n            ActorType = \"Person\"\n        };\n    }\n    \n    // Implement other required methods...\n}\n\n// Register in DI\nbuilder.Services.AddSingleton\u003cIIdentityProvider, CustomIdentityProvider\u003e();\n```\n\n## Requirements\n\n- .NET 9.0 or later\n- Docker (for containerized deployment)\n- A domain name with HTTPS for production federation\n\n## Testing \u0026 Quality Assurance\n\nBroca includes a comprehensive test suite to ensure reliability and standards compliance:\n\n**Integration Tests** (88+ tests across 10 test suites)\n- `ServerToServerTests` - Cross-server federation scenarios (22 tests)\n- `SharedInboxTests` - Efficient multi-user delivery (11 tests)\n- `ClientToServerTests` - Outbox posting and activity creation (7 tests)\n- `CustomCollectionsTests` - Manual and query-based collections (15 tests)\n- `AdminOperationsTests` - Back-channel user management (8 tests)\n- `ClientAuthenticationTests` - API key and HTTP Signature auth (5 tests)\n- `ContentTypeHandlingTests` - Mastodon compatibility (7 tests)\n- `BlobStorageTests` - Media upload and retrieval (4 tests)\n- `NodeInfoStatisticsTests` - Server metadata endpoints (5 tests)\n- `OutboxAuthenticationTests` - Security validation (3 tests)\n\n**Unit Tests**\n- Repository implementations (actors, activities, delivery queue)\n- Blob storage service functionality\n- Component rendering logic\n\nAll tests use real HTTP clients and in-memory servers to validate end-to-end behavior, not mocked implementations. This ensures that Broca works correctly with actual ActivityPub clients and servers in the fediverse.\n\n## Contributing\n\nContributions are welcome! Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for development workflow and guidelines.\n\n## License\n\n[Your license here]\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faluitink%2Fbroca","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faluitink%2Fbroca","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faluitink%2Fbroca/lists"}