diff --git a/README.md b/README.md index f543eb3..1501380 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # @astrojs/discovery -> Comprehensive discovery integration for Astro - handles robots.txt, llms.txt, humans.txt, security.txt, canary.txt, and sitemap generation +> Comprehensive discovery integration for Astro - handles robots.txt, llms.txt, humans.txt, security.txt, canary.txt, WebFinger, and sitemap generation ## Overview -This integration provides automatic generation of all standard discovery files for your Astro site, making it easily discoverable by search engines, LLMs, and humans, while providing security contact information and transparency mechanisms. +This integration provides automatic generation of all standard discovery files for your Astro site, making it easily discoverable by search engines, LLMs, humans, and federated services, while providing security contact information and transparency mechanisms. ## Features @@ -13,6 +13,7 @@ This integration provides automatic generation of all standard discovery files f - πŸ‘₯ **humans.txt** - Human-readable credits and tech stack - πŸ”’ **security.txt** - RFC 9116 compliant security contact info - 🐦 **canary.txt** - Warrant canary for transparency +- πŸ” **WebFinger** - RFC 7033 resource discovery (ActivityPub, OpenID) - πŸ—ΊοΈ **sitemap.xml** - Automatic sitemap generation - ⚑ **Dynamic URLs** - Adapts to your `site` config - 🎯 **Smart Caching** - Optimized cache headers @@ -450,6 +451,118 @@ discovery({ - Generates at `/.well-known/canary.txt` - See [CANARY_SPEC.md](./CANARY_SPEC.md) for full specification +##### `webfinger` + +Configuration for WebFinger resource discovery (RFC 7033). + +**Type:** +```typescript +interface WebFingerConfig { + enabled?: boolean; // Opt-in (default: false) + resources?: WebFingerResource[]; // Static resources + collections?: { // Content collection integration + name: string; // Collection name (e.g., 'team') + resourceTemplate: string; // URI template: 'acct:{slug}@example.com' + subjectTemplate?: string; // Defaults to resourceTemplate + linksBuilder?: (entry: any) => WebFingerLink[]; + aliasesBuilder?: (entry: any) => string[]; + propertiesBuilder?: (entry: any) => Record; + }[]; +} + +interface WebFingerResource { + resource: string; // Resource URI (acct:, https://, etc.) + subject?: string; // Subject (defaults to resource) + aliases?: string[]; // Alternative URIs + properties?: Record; // URI-based properties + links?: WebFingerLink[]; // Related links +} + +interface WebFingerLink { + rel: string; // Link relation (URI or IANA type) + href?: string; // Target URI + type?: string; // Media type + titles?: Record; // Titles with language tags + properties?: Record; +} +``` + +**Example (Static Resources):** +```typescript +discovery({ + webfinger: { + enabled: true, + resources: [ + { + resource: 'acct:alice@example.com', + aliases: ['https://example.com/@alice'], + links: [ + { + rel: 'http://webfinger.net/rel/profile-page', + type: 'text/html', + href: 'https://example.com/@alice' + }, + { + rel: 'self', + type: 'application/activity+json', // ActivityPub/Mastodon + href: 'https://example.com/users/alice' + } + ] + } + ] + } +}) +``` + +**Example (Content Collection):** +```typescript +discovery({ + webfinger: { + enabled: true, + collections: [{ + name: 'team', // Astro content collection + resourceTemplate: 'acct:{slug}@example.com', + linksBuilder: (member) => [ + { + rel: 'http://webfinger.net/rel/profile-page', + href: `https://example.com/team/${member.slug}`, + type: 'text/html' + }, + { + rel: 'http://webfinger.net/rel/avatar', + href: member.data.avatar, + type: 'image/jpeg' + } + ], + propertiesBuilder: (member) => ({ + 'http://schema.org/name': member.data.name, + 'http://schema.org/jobTitle': member.data.role + }) + }] + } +}) +``` + +**Common Use Cases:** +- **ActivityPub/Mastodon**: Enable federated social network discovery +- **OpenID Connect**: Provide issuer discovery for authentication +- **Team Profiles**: Make team members discoverable across services +- **Author Discovery**: Link blog authors to their profiles/social accounts + +**Query Format:** +``` +GET /.well-known/webfinger?resource=acct:alice@example.com +GET /.well-known/webfinger?resource=acct:alice@example.com&rel=self +``` + +**Notes:** +- Dynamic route - not prerendered +- Requires `?resource=` query parameter (RFC 7033) +- Optional `?rel=` parameter filters links +- CORS enabled (`Access-Control-Allow-Origin: *`) +- Media type: `application/jrd+json` +- Template vars: `{slug}`, `{id}`, `{data.fieldName}`, `{siteURL}` + ##### `sitemap` Configuration passed to `@astrojs/sitemap`. @@ -488,11 +601,12 @@ Configure HTTP cache headers for discovery files. **Type:** ```typescript interface CachingConfig { - robots?: number; // seconds + robots?: number; // seconds llms?: number; humans?: number; security?: number; canary?: number; + webfinger?: number; sitemap?: number; } ``` @@ -500,12 +614,13 @@ interface CachingConfig { **Default:** ```typescript { - robots: 3600, // 1 hour - llms: 3600, // 1 hour - humans: 86400, // 24 hours - security: 86400, // 24 hours - canary: 3600, // 1 hour (check frequently!) - sitemap: 3600 // 1 hour + robots: 3600, // 1 hour + llms: 3600, // 1 hour + humans: 86400, // 24 hours + security: 86400, // 24 hours + canary: 3600, // 1 hour (check frequently!) + webfinger: 3600, // 1 hour + sitemap: 3600 // 1 hour } ```