Merge reference documentation
Complete API reference for all configuration options - 11 reference pages covering entire API surface - TypeScript type definitions - Default values and examples - RFC compliance documentation
This commit is contained in:
commit
07ce65cf9e
@ -1,31 +1,295 @@
|
||||
---
|
||||
title: API Reference
|
||||
description: API and programmatic interface reference
|
||||
description: Programmatic API reference
|
||||
---
|
||||
|
||||
Complete API reference for programmatic usage of @astrojs/discovery.
|
||||
Programmatic API reference for `@astrojs/discovery`.
|
||||
|
||||
:::note[Work in Progress]
|
||||
This page is currently being developed. Check back soon for complete documentation.
|
||||
:::
|
||||
## Integration Function
|
||||
|
||||
## Coming Soon
|
||||
### `discovery(config?)`
|
||||
|
||||
This section will include:
|
||||
- Detailed explanations
|
||||
- Code examples
|
||||
- Best practices
|
||||
- Common patterns
|
||||
- Troubleshooting tips
|
||||
Main integration function for Astro.
|
||||
|
||||
## Related Pages
|
||||
```typescript
|
||||
function discovery(config?: DiscoveryConfig): AstroIntegration
|
||||
```
|
||||
|
||||
- [Configuration Reference](/reference/configuration/)
|
||||
- [API Reference](/reference/api/)
|
||||
- [Examples](/examples/ecommerce/)
|
||||
**Parameters:**
|
||||
- `config` (optional): Discovery configuration object
|
||||
|
||||
## Need Help?
|
||||
**Returns:** Astro integration object
|
||||
|
||||
- Check our [FAQ](/community/faq/)
|
||||
- Visit [Troubleshooting](/community/troubleshooting/)
|
||||
- Open an issue on [GitHub](https://github.com/withastro/astro-discovery/issues)
|
||||
**Example:**
|
||||
```typescript
|
||||
import { defineConfig } from 'astro/config';
|
||||
import discovery from '@astrojs/discovery';
|
||||
|
||||
export default defineConfig({
|
||||
site: 'https://example.com',
|
||||
integrations: [
|
||||
discovery({
|
||||
robots: { crawlDelay: 2 },
|
||||
llms: { description: 'My site' }
|
||||
})
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
## Generator Functions
|
||||
|
||||
These functions are used internally but can be imported for custom usage.
|
||||
|
||||
### `generateRobotsTxt(config, siteURL)`
|
||||
|
||||
Generate robots.txt content.
|
||||
|
||||
```typescript
|
||||
function generateRobotsTxt(
|
||||
config: RobotsConfig,
|
||||
siteURL: URL
|
||||
): string
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { generateRobotsTxt } from '@astrojs/discovery/generators';
|
||||
|
||||
const robotsTxt = generateRobotsTxt(
|
||||
{ crawlDelay: 2, allowAllBots: true },
|
||||
new URL('https://example.com')
|
||||
);
|
||||
```
|
||||
|
||||
### `generateLLMsTxt(config, siteURL)`
|
||||
|
||||
Generate llms.txt content (async).
|
||||
|
||||
```typescript
|
||||
function generateLLMsTxt(
|
||||
config: LLMsConfig,
|
||||
siteURL: URL
|
||||
): Promise<string>
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { generateLLMsTxt } from '@astrojs/discovery/generators';
|
||||
|
||||
const llmsTxt = await generateLLMsTxt(
|
||||
{ description: 'My site', keyFeatures: ['Feature 1'] },
|
||||
new URL('https://example.com')
|
||||
);
|
||||
```
|
||||
|
||||
### `generateHumansTxt(config)`
|
||||
|
||||
Generate humans.txt content.
|
||||
|
||||
```typescript
|
||||
function generateHumansTxt(
|
||||
config: HumansConfig
|
||||
): string
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { generateHumansTxt } from '@astrojs/discovery/generators';
|
||||
|
||||
const humansTxt = generateHumansTxt({
|
||||
team: [{ name: 'Alice', role: 'Developer' }]
|
||||
});
|
||||
```
|
||||
|
||||
### `generateSecurityTxt(config, siteURL)`
|
||||
|
||||
Generate security.txt content.
|
||||
|
||||
```typescript
|
||||
function generateSecurityTxt(
|
||||
config: SecurityConfig,
|
||||
siteURL: URL
|
||||
): string
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { generateSecurityTxt } from '@astrojs/discovery/generators';
|
||||
|
||||
const securityTxt = generateSecurityTxt(
|
||||
{ contact: 'security@example.com', expires: 'auto' },
|
||||
new URL('https://example.com')
|
||||
);
|
||||
```
|
||||
|
||||
### `generateCanaryTxt(config, siteURL)`
|
||||
|
||||
Generate canary.txt content.
|
||||
|
||||
```typescript
|
||||
function generateCanaryTxt(
|
||||
config: CanaryConfig,
|
||||
siteURL: URL
|
||||
): string
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { generateCanaryTxt } from '@astrojs/discovery/generators';
|
||||
|
||||
const canaryTxt = generateCanaryTxt(
|
||||
{ organization: 'Example Corp', frequency: 'monthly' },
|
||||
new URL('https://example.com')
|
||||
);
|
||||
```
|
||||
|
||||
### `generateWebFingerJRD(config, resource, rels, siteURL, getCollectionData?)`
|
||||
|
||||
Generate WebFinger JRD response (async).
|
||||
|
||||
```typescript
|
||||
function generateWebFingerJRD(
|
||||
config: WebFingerConfig,
|
||||
requestedResource: string,
|
||||
requestedRels: string[] | undefined,
|
||||
siteURL: URL,
|
||||
getCollectionData?: (collectionName: string) => Promise<any[]>
|
||||
): Promise<string | null>
|
||||
```
|
||||
|
||||
**Returns:** JRD JSON string or `null` if resource not found
|
||||
|
||||
## Validator Functions
|
||||
|
||||
### `validateConfig(userConfig)`
|
||||
|
||||
Validate and merge configuration with defaults.
|
||||
|
||||
```typescript
|
||||
function validateConfig(
|
||||
userConfig?: DiscoveryConfig
|
||||
): DiscoveryConfig
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { validateConfig } from '@astrojs/discovery/validators';
|
||||
|
||||
const config = validateConfig({
|
||||
robots: { crawlDelay: 2 }
|
||||
});
|
||||
// Returns merged config with defaults
|
||||
```
|
||||
|
||||
## Default Values
|
||||
|
||||
### Default Robots Config
|
||||
|
||||
```typescript
|
||||
const DEFAULT_ROBOTS_CONFIG = {
|
||||
enabled: true,
|
||||
crawlDelay: 1,
|
||||
allowAllBots: true,
|
||||
llmBots: {
|
||||
enabled: true
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Default LLM Bots
|
||||
|
||||
```typescript
|
||||
const DEFAULT_LLM_BOTS = [
|
||||
'Anthropic-AI',
|
||||
'Claude-Web',
|
||||
'GPTBot',
|
||||
'ChatGPT-User',
|
||||
'cohere-ai',
|
||||
'Google-Extended',
|
||||
'PerplexityBot',
|
||||
'Applebot-Extended'
|
||||
];
|
||||
```
|
||||
|
||||
### Default Cache Durations
|
||||
|
||||
```typescript
|
||||
const DEFAULT_CACHING_CONFIG = {
|
||||
robots: 3600, // 1 hour
|
||||
llms: 3600, // 1 hour
|
||||
humans: 86400, // 24 hours
|
||||
security: 86400, // 24 hours
|
||||
canary: 3600, // 1 hour
|
||||
webfinger: 3600, // 1 hour
|
||||
sitemap: 3600 // 1 hour
|
||||
};
|
||||
```
|
||||
|
||||
## Custom Templates
|
||||
|
||||
You can provide custom template functions:
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
templates: {
|
||||
robots: (config, siteURL) => {
|
||||
return `User-agent: *\nAllow: /\nSitemap: ${siteURL}/sitemap.xml`;
|
||||
},
|
||||
|
||||
llms: async (config, siteURL) => {
|
||||
const content = await fetchDynamicContent();
|
||||
return `# ${siteURL.hostname}\n\n${content}`;
|
||||
},
|
||||
|
||||
humans: (config, siteURL) => {
|
||||
return `/* TEAM */\n\n Developer: ${config.team[0].name}`;
|
||||
},
|
||||
|
||||
security: (config, siteURL) => {
|
||||
return `Contact: ${config.contact}\nExpires: ${config.expires}`;
|
||||
},
|
||||
|
||||
canary: (config, siteURL) => {
|
||||
return `Organization: ${config.organization}\nIssued: ${new Date().toISOString()}`;
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Programmatic File Generation
|
||||
|
||||
Generate files programmatically:
|
||||
|
||||
```typescript
|
||||
import {
|
||||
generateRobotsTxt,
|
||||
generateLLMsTxt,
|
||||
generateHumansTxt,
|
||||
generateSecurityTxt,
|
||||
generateCanaryTxt
|
||||
} from '@astrojs/discovery/generators';
|
||||
|
||||
const siteURL = new URL('https://example.com');
|
||||
|
||||
// Generate all files
|
||||
const robots = generateRobotsTxt({ crawlDelay: 2 }, siteURL);
|
||||
const llms = await generateLLMsTxt({ description: 'My site' }, siteURL);
|
||||
const humans = generateHumansTxt({ team: [{ name: 'Alice' }] });
|
||||
const security = generateSecurityTxt({ contact: 'security@example.com' }, siteURL);
|
||||
const canary = generateCanaryTxt({ organization: 'Example' }, siteURL);
|
||||
|
||||
// Write to files
|
||||
await fs.writeFile('public/robots.txt', robots);
|
||||
await fs.writeFile('public/llms.txt', llms);
|
||||
await fs.writeFile('public/humans.txt', humans);
|
||||
await fs.writeFile('public/.well-known/security.txt', security);
|
||||
await fs.writeFile('public/.well-known/canary.txt', canary);
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- All generator functions are pure and have no side effects
|
||||
- Validation happens automatically when using the integration
|
||||
- Custom templates override default generators
|
||||
- All async functions return Promises
|
||||
- WebFinger is dynamic and requires runtime query handling
|
||||
|
||||
@ -1,31 +1,178 @@
|
||||
---
|
||||
title: Cache Options
|
||||
title: Cache Configuration
|
||||
description: HTTP caching configuration reference
|
||||
---
|
||||
|
||||
Configure cache control headers for all discovery files.
|
||||
Configure HTTP cache control headers for all discovery files.
|
||||
|
||||
:::note[Work in Progress]
|
||||
This page is currently being developed. Check back soon for complete documentation.
|
||||
:::
|
||||
## CachingConfig
|
||||
|
||||
## Coming Soon
|
||||
```typescript
|
||||
interface CachingConfig {
|
||||
robots?: number;
|
||||
llms?: number;
|
||||
humans?: number;
|
||||
security?: number;
|
||||
canary?: number;
|
||||
webfinger?: number;
|
||||
sitemap?: number;
|
||||
}
|
||||
```
|
||||
|
||||
This section will include:
|
||||
- Detailed explanations
|
||||
- Code examples
|
||||
- Best practices
|
||||
- Common patterns
|
||||
- Troubleshooting tips
|
||||
## Properties
|
||||
|
||||
## Related Pages
|
||||
All properties are cache durations in seconds.
|
||||
|
||||
- [Configuration Reference](/reference/configuration/)
|
||||
- [API Reference](/reference/api/)
|
||||
- [Examples](/examples/ecommerce/)
|
||||
| Property | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `robots` | `3600` | robots.txt cache duration (1 hour) |
|
||||
| `llms` | `3600` | llms.txt cache duration (1 hour) |
|
||||
| `humans` | `86400` | humans.txt cache duration (24 hours) |
|
||||
| `security` | `86400` | security.txt cache duration (24 hours) |
|
||||
| `canary` | `3600` | canary.txt cache duration (1 hour) |
|
||||
| `webfinger` | `3600` | WebFinger cache duration (1 hour) |
|
||||
| `sitemap` | `3600` | Sitemap cache duration (1 hour) |
|
||||
|
||||
## Need Help?
|
||||
**Valid range:** 0 to 31536000 seconds (1 year)
|
||||
|
||||
- Check our [FAQ](/community/faq/)
|
||||
- Visit [Troubleshooting](/community/troubleshooting/)
|
||||
- Open an issue on [GitHub](https://github.com/withastro/astro-discovery/issues)
|
||||
## Examples
|
||||
|
||||
### Custom cache durations
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
caching: {
|
||||
robots: 7200, // 2 hours
|
||||
llms: 1800, // 30 minutes
|
||||
humans: 172800, // 48 hours
|
||||
security: 43200, // 12 hours
|
||||
canary: 1800, // 30 minutes (check frequently)
|
||||
webfinger: 7200, // 2 hours
|
||||
sitemap: 3600 // 1 hour
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Aggressive caching
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
caching: {
|
||||
robots: 86400, // 24 hours
|
||||
llms: 86400, // 24 hours
|
||||
humans: 604800, // 1 week
|
||||
security: 604800 // 1 week
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Minimal caching (development)
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
caching: {
|
||||
robots: 60, // 1 minute
|
||||
llms: 60, // 1 minute
|
||||
humans: 300, // 5 minutes
|
||||
security: 300, // 5 minutes
|
||||
canary: 60 // 1 minute
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### No caching
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
caching: {
|
||||
robots: 0,
|
||||
llms: 0,
|
||||
humans: 0,
|
||||
security: 0,
|
||||
canary: 0,
|
||||
webfinger: 0,
|
||||
sitemap: 0
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Environment-based caching
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
caching: {
|
||||
robots: import.meta.env.PROD ? 3600 : 60,
|
||||
llms: import.meta.env.PROD ? 3600 : 60,
|
||||
humans: import.meta.env.PROD ? 86400 : 300
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Canary-focused caching
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
caching: {
|
||||
canary: 1800, // 30 minutes - check frequently
|
||||
security: 86400, // 24 hours - stable
|
||||
humans: 604800, // 1 week - rarely changes
|
||||
robots: 3600, // 1 hour - moderate
|
||||
llms: 3600 // 1 hour - moderate
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Cache-Control Headers
|
||||
|
||||
The integration sets these HTTP headers:
|
||||
|
||||
```
|
||||
Cache-Control: public, max-age={duration}
|
||||
```
|
||||
|
||||
Where `{duration}` is the configured cache duration in seconds.
|
||||
|
||||
### Example Response
|
||||
|
||||
```http
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
Cache-Control: public, max-age=3600
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Balance freshness vs load:** Longer caches reduce server load but may show stale content
|
||||
2. **Canary should be short:** Check warrant canaries frequently (30 min to 1 hour)
|
||||
3. **Humans.txt can be long:** Team info rarely changes (24 hours to 1 week)
|
||||
4. **Security.txt moderate:** Balance between updates and load (12-24 hours)
|
||||
5. **Development vs production:** Use short caches in dev, longer in prod
|
||||
6. **Consider your update frequency:** Match cache to actual content update rate
|
||||
|
||||
## Time Conversion Reference
|
||||
|
||||
| Duration | Seconds |
|
||||
|----------|---------|
|
||||
| 1 minute | 60 |
|
||||
| 5 minutes | 300 |
|
||||
| 15 minutes | 900 |
|
||||
| 30 minutes | 1800 |
|
||||
| 1 hour | 3600 |
|
||||
| 2 hours | 7200 |
|
||||
| 6 hours | 21600 |
|
||||
| 12 hours | 43200 |
|
||||
| 24 hours (1 day) | 86400 |
|
||||
| 48 hours (2 days) | 172800 |
|
||||
| 1 week | 604800 |
|
||||
| 1 month (30 days) | 2592000 |
|
||||
| 1 year | 31536000 |
|
||||
|
||||
## Notes
|
||||
|
||||
- Caching is applied via `Cache-Control` headers
|
||||
- CDNs and browsers respect these directives
|
||||
- Set to `0` to disable caching
|
||||
- Maximum allowed: 31536000 seconds (1 year)
|
||||
- Validation warning if outside 0-31536000 range
|
||||
|
||||
@ -1,31 +1,324 @@
|
||||
---
|
||||
title: canary.txt Options
|
||||
description: Configuration reference for canary.txt
|
||||
title: canary.txt Configuration
|
||||
description: Configuration reference for canary.txt (warrant canary)
|
||||
---
|
||||
|
||||
Complete reference for warrant canary configuration options.
|
||||
Configuration reference for `/.well-known/canary.txt` generation.
|
||||
|
||||
:::note[Work in Progress]
|
||||
This page is currently being developed. Check back soon for complete documentation.
|
||||
:::
|
||||
## CanaryConfig
|
||||
|
||||
## Coming Soon
|
||||
```typescript
|
||||
interface CanaryConfig {
|
||||
enabled?: boolean;
|
||||
organization?: string;
|
||||
contact?: string;
|
||||
frequency?: 'daily' | 'weekly' | 'monthly' | 'quarterly' | 'yearly';
|
||||
expires?: string | 'auto';
|
||||
statements?: CanaryStatement[] | (() => CanaryStatement[]);
|
||||
additionalStatement?: string;
|
||||
verification?: string;
|
||||
previousCanary?: string;
|
||||
blockchainProof?: {
|
||||
network: string;
|
||||
address: string;
|
||||
txHash?: string;
|
||||
timestamp?: string;
|
||||
};
|
||||
personnelStatement?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
This section will include:
|
||||
- Detailed explanations
|
||||
- Code examples
|
||||
- Best practices
|
||||
- Common patterns
|
||||
- Troubleshooting tips
|
||||
## Properties
|
||||
|
||||
## Related Pages
|
||||
### enabled
|
||||
|
||||
- [Configuration Reference](/reference/configuration/)
|
||||
- [API Reference](/reference/api/)
|
||||
- [Examples](/examples/ecommerce/)
|
||||
- **Type:** `boolean`
|
||||
- **Default:** `true`
|
||||
- **Description:** Enable or disable canary.txt generation
|
||||
|
||||
## Need Help?
|
||||
### organization
|
||||
|
||||
- Check our [FAQ](/community/faq/)
|
||||
- Visit [Troubleshooting](/community/troubleshooting/)
|
||||
- Open an issue on [GitHub](https://github.com/withastro/astro-discovery/issues)
|
||||
- **Type:** `string`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Organization name
|
||||
|
||||
### contact
|
||||
|
||||
- **Type:** `string`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Contact email for canary inquiries
|
||||
- **Note:** Email addresses automatically get `mailto:` prefix
|
||||
|
||||
### frequency
|
||||
|
||||
- **Type:** `'daily' | 'weekly' | 'monthly' | 'quarterly' | 'yearly'`
|
||||
- **Default:** `'monthly'`
|
||||
- **Description:** How often the canary is updated
|
||||
|
||||
**Auto-calculated expiration based on frequency:**
|
||||
- `daily`: 2 days
|
||||
- `weekly`: 10 days
|
||||
- `monthly`: 35 days
|
||||
- `quarterly`: 100 days
|
||||
- `yearly`: 380 days
|
||||
|
||||
### expires
|
||||
|
||||
- **Type:** `string | 'auto'`
|
||||
- **Default:** `'auto'` (calculated from frequency)
|
||||
- **Description:** Expiration date in ISO 8601 format
|
||||
|
||||
### statements
|
||||
|
||||
- **Type:** `CanaryStatement[] | (() => CanaryStatement[])`
|
||||
- **Default:** Default statements (NSL, FISA, gag orders, surveillance, backdoors)
|
||||
- **Description:** Statements about what has NOT been received
|
||||
|
||||
**CanaryStatement interface:**
|
||||
```typescript
|
||||
interface CanaryStatement {
|
||||
type: 'nsl' | 'fisa' | 'gag' | 'surveillance' | 'backdoor' | 'encryption' | 'other';
|
||||
description: string;
|
||||
received: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
**Important:** Only statements with `received: false` appear in the canary.
|
||||
|
||||
**Default statements:**
|
||||
```typescript
|
||||
[
|
||||
{ type: 'nsl', description: 'National Security Letters (NSLs)', received: false },
|
||||
{ type: 'fisa', description: 'FISA court orders', received: false },
|
||||
{ type: 'gag', description: 'Gag orders preventing disclosure', received: false },
|
||||
{ type: 'surveillance', description: 'Secret government requests for user data', received: false },
|
||||
{ type: 'backdoor', description: 'Requests to install surveillance capabilities', received: false }
|
||||
]
|
||||
```
|
||||
|
||||
**Custom statements:**
|
||||
```typescript
|
||||
discovery({
|
||||
canary: {
|
||||
organization: 'Example Corp',
|
||||
statements: [
|
||||
{ type: 'nsl', description: 'National Security Letters', received: false },
|
||||
{ type: 'gag', description: 'Gag orders', received: false },
|
||||
{ type: 'other', description: 'Requests for user encryption keys', received: false }
|
||||
]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**Dynamic statements:**
|
||||
```typescript
|
||||
discovery({
|
||||
canary: {
|
||||
organization: 'Example Corp',
|
||||
statements: () => {
|
||||
// Generate statements dynamically at build time
|
||||
return [
|
||||
{ type: 'nsl', description: 'NSLs', received: false },
|
||||
{ type: 'gag', description: 'Gag orders', received: false }
|
||||
];
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### additionalStatement
|
||||
|
||||
- **Type:** `string`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Additional free-form statement text
|
||||
|
||||
### verification
|
||||
|
||||
- **Type:** `string`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** URL to PGP signature or other verification method
|
||||
|
||||
### previousCanary
|
||||
|
||||
- **Type:** `string`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** URL to previous canary for continuity verification
|
||||
|
||||
### blockchainProof
|
||||
|
||||
- **Type:** `{ network: string; address: string; txHash?: string; timestamp?: string }`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Blockchain proof for tamper-evident verification
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
discovery({
|
||||
canary: {
|
||||
organization: 'Example Corp',
|
||||
blockchainProof: {
|
||||
network: 'Ethereum',
|
||||
address: '0x1234...5678',
|
||||
txHash: '0xabcd...ef01',
|
||||
timestamp: '2025-11-08T12:00:00Z'
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### personnelStatement
|
||||
|
||||
- **Type:** `boolean`
|
||||
- **Default:** `false`
|
||||
- **Description:** Include statement about key personnel being free from duress
|
||||
|
||||
**Generated text when `true`:**
|
||||
```
|
||||
Key Personnel Statement: All key personnel with access to
|
||||
infrastructure remain free and under no duress.
|
||||
```
|
||||
|
||||
## Generated Output
|
||||
|
||||
**Minimal configuration:**
|
||||
```typescript
|
||||
discovery({
|
||||
canary: {
|
||||
organization: 'Example Corp',
|
||||
contact: 'canary@example.com'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
Canonical-URL: https://example.com/.well-known/canary.txt
|
||||
Issued: 2025-11-08T12:00:00.000Z
|
||||
Expires: 2025-12-13T12:00:00.000Z
|
||||
Organization: Example Corp
|
||||
Contact: mailto:canary@example.com
|
||||
Frequency: monthly
|
||||
|
||||
Statement: As of 2025-11-08, Example Corp has NOT received:
|
||||
- National Security Letters (NSLs)
|
||||
- FISA court orders
|
||||
- Gag orders preventing disclosure
|
||||
- Secret government requests for user data
|
||||
- Requests to install surveillance capabilities
|
||||
|
||||
This canary will be updated monthly. Absence of an update
|
||||
within 35 days should be considered significant.
|
||||
|
||||
---
|
||||
|
||||
This warrant canary follows the proposed canary.txt specification.
|
||||
See: https://github.com/withastro/astro-discovery/blob/main/CANARY_SPEC.md
|
||||
```
|
||||
|
||||
**Full configuration:**
|
||||
```typescript
|
||||
discovery({
|
||||
canary: {
|
||||
organization: 'Example Corp',
|
||||
contact: 'canary@example.com',
|
||||
frequency: 'weekly',
|
||||
statements: [
|
||||
{ type: 'nsl', description: 'National Security Letters', received: false },
|
||||
{ type: 'gag', description: 'Gag orders', received: false }
|
||||
],
|
||||
additionalStatement: 'We are committed to transparency and user privacy.',
|
||||
verification: 'https://example.com/canary.txt.asc',
|
||||
previousCanary: 'https://example.com/canary/2025-11-01.txt',
|
||||
blockchainProof: {
|
||||
network: 'Ethereum',
|
||||
address: '0x1234567890abcdef',
|
||||
txHash: '0xabcdef1234567890'
|
||||
},
|
||||
personnelStatement: true
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Monthly canary
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
canary: {
|
||||
organization: 'Example Corp',
|
||||
contact: 'canary@example.com',
|
||||
frequency: 'monthly'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### With blockchain verification
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
canary: {
|
||||
organization: 'Example Corp',
|
||||
frequency: 'monthly',
|
||||
blockchainProof: {
|
||||
network: 'Bitcoin',
|
||||
address: 'bc1q...',
|
||||
txHash: process.env.CANARY_TX_HASH
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### With PGP signature
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
canary: {
|
||||
organization: 'Example Corp',
|
||||
contact: 'canary@example.com',
|
||||
frequency: 'monthly',
|
||||
verification: 'https://example.com/canary.txt.asc',
|
||||
previousCanary: 'https://example.com/canary/previous.txt'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Custom statements only
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
canary: {
|
||||
organization: 'Example Corp',
|
||||
frequency: 'quarterly',
|
||||
statements: [
|
||||
{ type: 'other', description: 'Demands for customer data', received: false },
|
||||
{ type: 'other', description: 'Requests to weaken encryption', received: false }
|
||||
],
|
||||
personnelStatement: true
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Update regularly:** Match your actual update capability to frequency setting
|
||||
2. **Verify authenticity:** Use PGP signatures or blockchain proofs
|
||||
3. **Maintain continuity:** Link to previous canaries
|
||||
4. **Be specific:** Customize statements to your threat model
|
||||
5. **Automate updates:** Integrate into CI/CD to ensure regular updates
|
||||
6. **Monitor expiration:** Set alerts before expiration dates
|
||||
7. **Document process:** Make canary updates part of your security procedures
|
||||
|
||||
## Important Notes
|
||||
|
||||
1. **Legal implications:** Warrant canaries have unclear legal status in some jurisdictions
|
||||
2. **Absence is significant:** A missing or expired canary may signal issues
|
||||
3. **Not a guarantee:** Canaries can be compelled to continue
|
||||
4. **Supplement, don't replace:** Use alongside other transparency measures
|
||||
5. **Update discipline:** Missing an update defeats the purpose
|
||||
|
||||
## Output Location
|
||||
|
||||
- **File:** `/.well-known/canary.txt`
|
||||
- **URL:** `https://example.com/.well-known/canary.txt`
|
||||
- **Cache-Control:** `public, max-age=3600` (1 hour, configurable via [caching](/reference/cache/))
|
||||
- **Specification:** See [CANARY_SPEC.md](https://github.com/withastro/astro-discovery/blob/main/CANARY_SPEC.md)
|
||||
|
||||
@ -3,29 +3,92 @@ title: Configuration Options
|
||||
description: Complete reference for all configuration options
|
||||
---
|
||||
|
||||
Comprehensive reference documentation for all available configuration options.
|
||||
Complete configuration reference for the `@astrojs/discovery` integration.
|
||||
|
||||
:::note[Work in Progress]
|
||||
This page is currently being developed. Check back soon for complete documentation.
|
||||
:::
|
||||
## DiscoveryConfig
|
||||
|
||||
## Coming Soon
|
||||
Main configuration interface passed to the `discovery()` integration function.
|
||||
|
||||
This section will include:
|
||||
- Detailed explanations
|
||||
- Code examples
|
||||
- Best practices
|
||||
- Common patterns
|
||||
- Troubleshooting tips
|
||||
```typescript
|
||||
interface DiscoveryConfig {
|
||||
robots?: RobotsConfig;
|
||||
llms?: LLMsConfig;
|
||||
humans?: HumansConfig;
|
||||
security?: SecurityConfig;
|
||||
canary?: CanaryConfig;
|
||||
webfinger?: WebFingerConfig;
|
||||
sitemap?: SitemapConfig;
|
||||
caching?: CachingConfig;
|
||||
templates?: TemplateConfig;
|
||||
}
|
||||
```
|
||||
|
||||
## Related Pages
|
||||
### Type Parameters
|
||||
|
||||
- [Configuration Reference](/reference/configuration/)
|
||||
- [API Reference](/reference/api/)
|
||||
- [Examples](/examples/ecommerce/)
|
||||
All configuration sections are optional. If omitted, defaults are used.
|
||||
|
||||
## Need Help?
|
||||
| Property | Type | Required | Default |
|
||||
|----------|------|----------|---------|
|
||||
| `robots` | `RobotsConfig` | No | `{ enabled: true, crawlDelay: 1, allowAllBots: true }` |
|
||||
| `llms` | `LLMsConfig` | No | `{ enabled: true }` |
|
||||
| `humans` | `HumansConfig` | No | `{ enabled: true }` |
|
||||
| `security` | `SecurityConfig` | No | `undefined` (disabled) |
|
||||
| `canary` | `CanaryConfig` | No | `undefined` (disabled) |
|
||||
| `webfinger` | `WebFingerConfig` | No | `undefined` (disabled) |
|
||||
| `sitemap` | `SitemapConfig` | No | `{}` |
|
||||
| `caching` | `CachingConfig` | No | See [Cache Reference](/reference/cache/) |
|
||||
| `templates` | `TemplateConfig` | No | `undefined` |
|
||||
|
||||
- Check our [FAQ](/community/faq/)
|
||||
- Visit [Troubleshooting](/community/troubleshooting/)
|
||||
- Open an issue on [GitHub](https://github.com/withastro/astro-discovery/issues)
|
||||
## Complete Example
|
||||
|
||||
```typescript
|
||||
import { defineConfig } from 'astro/config';
|
||||
import discovery from '@astrojs/discovery';
|
||||
|
||||
export default defineConfig({
|
||||
site: 'https://example.com',
|
||||
integrations: [
|
||||
discovery({
|
||||
robots: {
|
||||
crawlDelay: 2,
|
||||
allowAllBots: true,
|
||||
llmBots: {
|
||||
enabled: true,
|
||||
agents: ['CustomBot']
|
||||
}
|
||||
},
|
||||
llms: {
|
||||
description: 'Site description',
|
||||
keyFeatures: ['Feature 1', 'Feature 2']
|
||||
},
|
||||
humans: {
|
||||
team: [
|
||||
{ name: 'Developer', role: 'Creator' }
|
||||
]
|
||||
},
|
||||
security: {
|
||||
contact: 'security@example.com',
|
||||
expires: 'auto'
|
||||
},
|
||||
caching: {
|
||||
robots: 3600,
|
||||
llms: 1800
|
||||
}
|
||||
})
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
## Configuration Sections
|
||||
|
||||
Detailed configuration for each section:
|
||||
|
||||
- [Robots Configuration](/reference/robots/) - robots.txt generation
|
||||
- [LLMs Configuration](/reference/llms/) - llms.txt generation
|
||||
- [Humans Configuration](/reference/humans/) - humans.txt generation
|
||||
- [Security Configuration](/reference/security/) - security.txt generation
|
||||
- [Canary Configuration](/reference/canary/) - canary.txt generation
|
||||
- [WebFinger Configuration](/reference/webfinger/) - WebFinger discovery
|
||||
- [Sitemap Configuration](/reference/sitemap/) - Sitemap generation
|
||||
- [Cache Configuration](/reference/cache/) - HTTP caching
|
||||
- [TypeScript Types](/reference/typescript/) - Complete type definitions
|
||||
|
||||
@ -1,31 +1,363 @@
|
||||
---
|
||||
title: humans.txt Options
|
||||
description: Configuration reference for humans.txt
|
||||
title: humans.txt Configuration
|
||||
description: Configuration reference for humans.txt generation
|
||||
---
|
||||
|
||||
Full reference for humans.txt configuration and formatting options.
|
||||
Configuration reference for `/humans.txt` generation.
|
||||
|
||||
:::note[Work in Progress]
|
||||
This page is currently being developed. Check back soon for complete documentation.
|
||||
:::
|
||||
## HumansConfig
|
||||
|
||||
## Coming Soon
|
||||
```typescript
|
||||
interface HumansConfig {
|
||||
enabled?: boolean;
|
||||
team?: TeamMember[];
|
||||
thanks?: string[];
|
||||
site?: SiteInfo;
|
||||
story?: string;
|
||||
funFacts?: string[];
|
||||
philosophy?: string[];
|
||||
customSections?: Record<string, string>;
|
||||
}
|
||||
```
|
||||
|
||||
This section will include:
|
||||
- Detailed explanations
|
||||
- Code examples
|
||||
- Best practices
|
||||
- Common patterns
|
||||
- Troubleshooting tips
|
||||
## Properties
|
||||
|
||||
## Related Pages
|
||||
### enabled
|
||||
|
||||
- [Configuration Reference](/reference/configuration/)
|
||||
- [API Reference](/reference/api/)
|
||||
- [Examples](/examples/ecommerce/)
|
||||
- **Type:** `boolean`
|
||||
- **Default:** `true`
|
||||
- **Description:** Enable or disable humans.txt generation
|
||||
|
||||
## Need Help?
|
||||
### team
|
||||
|
||||
- Check our [FAQ](/community/faq/)
|
||||
- Visit [Troubleshooting](/community/troubleshooting/)
|
||||
- Open an issue on [GitHub](https://github.com/withastro/astro-discovery/issues)
|
||||
- **Type:** `TeamMember[]`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Team members who built the site
|
||||
|
||||
**TeamMember interface:**
|
||||
```typescript
|
||||
interface TeamMember {
|
||||
name: string;
|
||||
role?: string;
|
||||
contact?: string;
|
||||
location?: string;
|
||||
twitter?: string;
|
||||
github?: string;
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
discovery({
|
||||
humans: {
|
||||
team: [
|
||||
{
|
||||
name: 'Alice Developer',
|
||||
role: 'Lead Developer',
|
||||
contact: 'alice@example.com',
|
||||
location: 'New York, NY',
|
||||
twitter: '@alice_dev',
|
||||
github: 'alice-dev'
|
||||
},
|
||||
{
|
||||
name: 'Bob Designer',
|
||||
role: 'UI/UX Designer',
|
||||
contact: 'bob@example.com',
|
||||
location: 'San Francisco, CA'
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### thanks
|
||||
|
||||
- **Type:** `string[]`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Thank you notes and acknowledgments
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
discovery({
|
||||
humans: {
|
||||
thanks: [
|
||||
'The Astro team for an amazing framework',
|
||||
'Our amazing community contributors',
|
||||
'Stack Overflow (obviously)',
|
||||
'Coffee, lots of coffee'
|
||||
]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### site
|
||||
|
||||
- **Type:** `SiteInfo`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Site technical information
|
||||
|
||||
**SiteInfo interface:**
|
||||
```typescript
|
||||
interface SiteInfo {
|
||||
lastUpdate?: string | 'auto';
|
||||
language?: string;
|
||||
doctype?: string;
|
||||
ide?: string;
|
||||
techStack?: string[];
|
||||
standards?: string[];
|
||||
components?: string[];
|
||||
software?: string[];
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
discovery({
|
||||
humans: {
|
||||
site: {
|
||||
lastUpdate: 'auto', // Auto-uses current date
|
||||
language: 'English',
|
||||
doctype: 'HTML5',
|
||||
ide: 'VS Code',
|
||||
techStack: ['Astro', 'TypeScript', 'React', 'Tailwind CSS'],
|
||||
standards: ['HTML5', 'CSS3', 'ES2023', 'WCAG 2.1'],
|
||||
components: ['@astrojs/react', '@astrojs/tailwind'],
|
||||
software: ['Docker', 'GitHub Actions', 'Vercel']
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**Note:** `lastUpdate: 'auto'` automatically uses the current build date.
|
||||
|
||||
### story
|
||||
|
||||
- **Type:** `string`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Project story or history (multi-line supported)
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
discovery({
|
||||
humans: {
|
||||
story: `
|
||||
This project started when we realized there was no good way to track
|
||||
sustainable products online. After months of research and development,
|
||||
we built a platform that not only helps consumers make better choices
|
||||
but also rewards companies for their environmental efforts.
|
||||
|
||||
Built with love during nights and weekends over 6 months.
|
||||
`.trim()
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### funFacts
|
||||
|
||||
- **Type:** `string[]`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Fun facts about the project
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
discovery({
|
||||
humans: {
|
||||
funFacts: [
|
||||
'Built entirely on a mechanical keyboard',
|
||||
'Fueled by 347 cups of coffee',
|
||||
'Started at a 48-hour hackathon',
|
||||
'The first commit was on a flight to Tokyo',
|
||||
'Tested by 1,000+ beta users before launch'
|
||||
]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### philosophy
|
||||
|
||||
- **Type:** `string[]`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Development philosophy statements
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
discovery({
|
||||
humans: {
|
||||
philosophy: [
|
||||
'Simple is better than complex',
|
||||
'Make it work, make it right, make it fast',
|
||||
'User experience over developer convenience',
|
||||
'Open source by default',
|
||||
'Leave the web better than we found it'
|
||||
]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### customSections
|
||||
|
||||
- **Type:** `Record<string, string>`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Custom sections to add
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
discovery({
|
||||
humans: {
|
||||
customSections: {
|
||||
'CREDITS': `
|
||||
Special thanks to:
|
||||
- Photography by Jane Smith
|
||||
- Icons by FontAwesome
|
||||
- Illustrations by UnDraw
|
||||
`.trim(),
|
||||
|
||||
'CONTACT': `
|
||||
Questions or feedback?
|
||||
Email us at: hello@example.com
|
||||
`.trim()
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Generated Output Structure
|
||||
|
||||
```
|
||||
/* TEAM */
|
||||
|
||||
Name: Alice Developer
|
||||
Role: Lead Developer
|
||||
Contact: alice@example.com
|
||||
From: New York, NY
|
||||
Twitter: @alice_dev
|
||||
GitHub: alice-dev
|
||||
|
||||
Name: Bob Designer
|
||||
Role: UI/UX Designer
|
||||
Contact: bob@example.com
|
||||
From: San Francisco, CA
|
||||
|
||||
/* THANKS */
|
||||
|
||||
The Astro team for an amazing framework
|
||||
Our amazing community contributors
|
||||
Stack Overflow (obviously)
|
||||
Coffee, lots of coffee
|
||||
|
||||
/* SITE */
|
||||
|
||||
Last update: 2025-11-08
|
||||
Language: English
|
||||
Doctype: HTML5
|
||||
IDE: VS Code
|
||||
Tech Stack: Astro, TypeScript, React, Tailwind CSS
|
||||
Standards: HTML5, CSS3, ES2023, WCAG 2.1
|
||||
Components: @astrojs/react, @astrojs/tailwind
|
||||
Software: Docker, GitHub Actions, Vercel
|
||||
|
||||
/* THE STORY */
|
||||
|
||||
This project started when we realized there was no good way to track
|
||||
sustainable products online. After months of research and development,
|
||||
we built a platform that not only helps consumers make better choices
|
||||
but also rewards companies for their environmental efforts.
|
||||
|
||||
Built with love during nights and weekends over 6 months.
|
||||
|
||||
/* FUN FACTS */
|
||||
|
||||
Built entirely on a mechanical keyboard
|
||||
Fueled by 347 cups of coffee
|
||||
Started at a 48-hour hackathon
|
||||
|
||||
/* PHILOSOPHY */
|
||||
|
||||
"Simple is better than complex"
|
||||
"Make it work, make it right, make it fast"
|
||||
"User experience over developer convenience"
|
||||
|
||||
/* CUSTOM SECTION */
|
||||
|
||||
Custom content here
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Minimal team information
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
humans: {
|
||||
team: [
|
||||
{
|
||||
name: 'Development Team',
|
||||
contact: 'dev@example.com'
|
||||
}
|
||||
],
|
||||
thanks: [
|
||||
'Open source community',
|
||||
'Early adopters'
|
||||
]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Full site details
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
humans: {
|
||||
team: [
|
||||
{ name: 'Alice', role: 'Developer', github: 'alice' },
|
||||
{ name: 'Bob', role: 'Designer', twitter: '@bob' }
|
||||
],
|
||||
site: {
|
||||
lastUpdate: 'auto',
|
||||
language: 'English',
|
||||
doctype: 'HTML5',
|
||||
techStack: ['Astro', 'React', 'TypeScript'],
|
||||
standards: ['HTML5', 'WCAG 2.1']
|
||||
},
|
||||
story: 'Built with passion over 6 months',
|
||||
funFacts: [
|
||||
'First commit was on a plane',
|
||||
'500+ cups of coffee consumed'
|
||||
]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Solo developer
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
humans: {
|
||||
team: [
|
||||
{
|
||||
name: 'Jane Hacker',
|
||||
role: 'Solo Developer & Designer',
|
||||
contact: 'jane@example.com',
|
||||
location: 'Remote',
|
||||
github: 'jane-hacker'
|
||||
}
|
||||
],
|
||||
thanks: [
|
||||
'My cat for moral support',
|
||||
'Stack Overflow',
|
||||
'The Astro community'
|
||||
],
|
||||
philosophy: [
|
||||
'Ship it',
|
||||
'Iterate quickly',
|
||||
'Listen to users'
|
||||
]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Output Location
|
||||
|
||||
- **File:** `/humans.txt`
|
||||
- **URL:** `https://example.com/humans.txt`
|
||||
- **Cache-Control:** `public, max-age=86400` (24 hours, configurable via [caching](/reference/cache/))
|
||||
|
||||
@ -1,31 +1,402 @@
|
||||
---
|
||||
title: llms.txt Options
|
||||
description: Configuration reference for llms.txt
|
||||
title: llms.txt Configuration
|
||||
description: Configuration reference for llms.txt generation
|
||||
---
|
||||
|
||||
Complete reference for llms.txt configuration options and structure.
|
||||
Configuration reference for `/llms.txt` generation.
|
||||
|
||||
:::note[Work in Progress]
|
||||
This page is currently being developed. Check back soon for complete documentation.
|
||||
:::
|
||||
## LLMsConfig
|
||||
|
||||
## Coming Soon
|
||||
```typescript
|
||||
interface LLMsConfig {
|
||||
enabled?: boolean;
|
||||
description?: string | (() => string);
|
||||
keyFeatures?: string[];
|
||||
importantPages?: ImportantPage[] | (() => Promise<ImportantPage[]>);
|
||||
instructions?: string;
|
||||
apiEndpoints?: APIEndpoint[];
|
||||
techStack?: TechStack;
|
||||
brandVoice?: string[];
|
||||
customSections?: Record<string, string>;
|
||||
}
|
||||
```
|
||||
|
||||
This section will include:
|
||||
- Detailed explanations
|
||||
- Code examples
|
||||
- Best practices
|
||||
- Common patterns
|
||||
- Troubleshooting tips
|
||||
## Properties
|
||||
|
||||
## Related Pages
|
||||
### enabled
|
||||
|
||||
- [Configuration Reference](/reference/configuration/)
|
||||
- [API Reference](/reference/api/)
|
||||
- [Examples](/examples/ecommerce/)
|
||||
- **Type:** `boolean`
|
||||
- **Default:** `true`
|
||||
- **Description:** Enable or disable llms.txt generation
|
||||
|
||||
## Need Help?
|
||||
```typescript
|
||||
discovery({
|
||||
llms: {
|
||||
enabled: false // Disable llms.txt
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
- Check our [FAQ](/community/faq/)
|
||||
- Visit [Troubleshooting](/community/troubleshooting/)
|
||||
- Open an issue on [GitHub](https://github.com/withastro/astro-discovery/issues)
|
||||
### description
|
||||
|
||||
- **Type:** `string | (() => string)`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Site description for AI assistants (can be dynamic function)
|
||||
|
||||
**Static example:**
|
||||
```typescript
|
||||
discovery({
|
||||
llms: {
|
||||
description: 'E-commerce platform for sustainable products'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**Dynamic example:**
|
||||
```typescript
|
||||
discovery({
|
||||
llms: {
|
||||
description: () => {
|
||||
const pkg = JSON.parse(fs.readFileSync('./package.json', 'utf-8'));
|
||||
return `${pkg.name} - ${pkg.description}`;
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### keyFeatures
|
||||
|
||||
- **Type:** `string[]`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Key features of the site
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
discovery({
|
||||
llms: {
|
||||
keyFeatures: [
|
||||
'AI-powered product recommendations',
|
||||
'Carbon footprint calculator',
|
||||
'Subscription management',
|
||||
'Real-time inventory tracking'
|
||||
]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### importantPages
|
||||
|
||||
- **Type:** `ImportantPage[] | (() => Promise<ImportantPage[]>)`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Important pages for AI to know about
|
||||
|
||||
**ImportantPage interface:**
|
||||
```typescript
|
||||
interface ImportantPage {
|
||||
name: string;
|
||||
path: string;
|
||||
description?: string;
|
||||
}
|
||||
```
|
||||
|
||||
**Static example:**
|
||||
```typescript
|
||||
discovery({
|
||||
llms: {
|
||||
importantPages: [
|
||||
{
|
||||
name: 'API Documentation',
|
||||
path: '/docs/api',
|
||||
description: 'Complete API reference'
|
||||
},
|
||||
{
|
||||
name: 'Getting Started',
|
||||
path: '/docs/getting-started'
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**Dynamic example with content collections:**
|
||||
```typescript
|
||||
import { getCollection } from 'astro:content';
|
||||
|
||||
discovery({
|
||||
llms: {
|
||||
importantPages: async () => {
|
||||
const docs = await getCollection('docs');
|
||||
return docs
|
||||
.filter(doc => doc.data.featured)
|
||||
.map(doc => ({
|
||||
name: doc.data.title,
|
||||
path: `/docs/${doc.slug}`,
|
||||
description: doc.data.description
|
||||
}));
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### instructions
|
||||
|
||||
- **Type:** `string`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Instructions for AI assistants when helping users
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
discovery({
|
||||
llms: {
|
||||
instructions: `
|
||||
When helping users with this site:
|
||||
1. Check API documentation first at /docs/api
|
||||
2. Use the /api/search endpoint for product queries
|
||||
3. Format responses in markdown
|
||||
4. Include relevant links to documentation
|
||||
5. Suggest sustainable alternatives when appropriate
|
||||
`.trim()
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### apiEndpoints
|
||||
|
||||
- **Type:** `APIEndpoint[]`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** API endpoints available
|
||||
|
||||
**APIEndpoint interface:**
|
||||
```typescript
|
||||
interface APIEndpoint {
|
||||
path: string;
|
||||
method?: string; // Default: 'GET'
|
||||
description: string;
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
discovery({
|
||||
llms: {
|
||||
apiEndpoints: [
|
||||
{
|
||||
path: '/api/products',
|
||||
method: 'GET',
|
||||
description: 'List all products with filters'
|
||||
},
|
||||
{
|
||||
path: '/api/search',
|
||||
method: 'POST',
|
||||
description: 'Search products and documentation'
|
||||
},
|
||||
{
|
||||
path: '/api/calculate-footprint',
|
||||
method: 'POST',
|
||||
description: 'Calculate carbon footprint for cart'
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### techStack
|
||||
|
||||
- **Type:** `TechStack`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Technology stack information
|
||||
|
||||
**TechStack interface:**
|
||||
```typescript
|
||||
interface TechStack {
|
||||
frontend?: string[];
|
||||
backend?: string[];
|
||||
ai?: string[];
|
||||
other?: string[];
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
discovery({
|
||||
llms: {
|
||||
techStack: {
|
||||
frontend: ['Astro', 'React', 'TypeScript', 'Tailwind CSS'],
|
||||
backend: ['Node.js', 'PostgreSQL', 'Redis'],
|
||||
ai: ['OpenAI GPT-4', 'Anthropic Claude'],
|
||||
other: ['Docker', 'Stripe', 'SendGrid']
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### brandVoice
|
||||
|
||||
- **Type:** `string[]`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Brand voice guidelines for AI assistants
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
discovery({
|
||||
llms: {
|
||||
brandVoice: [
|
||||
'Professional but friendly',
|
||||
'Technical but accessible',
|
||||
'Focus on sustainability',
|
||||
'Use concrete examples',
|
||||
'Avoid jargon when possible'
|
||||
]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### customSections
|
||||
|
||||
- **Type:** `Record<string, string>`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Custom sections to add to llms.txt
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
discovery({
|
||||
llms: {
|
||||
customSections: {
|
||||
'Pricing Information': `
|
||||
All products include:
|
||||
- Free shipping on orders over $50
|
||||
- 30-day return policy
|
||||
- Carbon offset for all shipments
|
||||
`.trim(),
|
||||
|
||||
'Support Channels': `
|
||||
- Email: support@example.com
|
||||
- Chat: Available 9am-5pm EST
|
||||
- Forum: https://example.com/forum
|
||||
`.trim()
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Generated Output Structure
|
||||
|
||||
```
|
||||
# example.com
|
||||
|
||||
> Site description goes here
|
||||
|
||||
---
|
||||
|
||||
## Site Information
|
||||
|
||||
- **URL**: https://example.com
|
||||
- **Description**: Site description
|
||||
|
||||
## Key Features
|
||||
|
||||
- Feature 1
|
||||
- Feature 2
|
||||
|
||||
## Important Pages
|
||||
|
||||
- **[Page Name](https://example.com/path)**
|
||||
Description of the page
|
||||
|
||||
## Instructions for AI Assistants
|
||||
|
||||
Instructions text...
|
||||
|
||||
## API Endpoints
|
||||
|
||||
- `GET /api/endpoint`
|
||||
Description
|
||||
Full URL: https://example.com/api/endpoint
|
||||
|
||||
## Technical Stack
|
||||
|
||||
- **Frontend**: Astro, React
|
||||
- **Backend**: Node.js, PostgreSQL
|
||||
- **AI/ML**: OpenAI GPT-4
|
||||
|
||||
## Brand Voice & Guidelines
|
||||
|
||||
- Guideline 1
|
||||
- Guideline 2
|
||||
|
||||
## Custom Section Title
|
||||
|
||||
Custom section content...
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-11-08
|
||||
|
||||
*This file was generated by [@astrojs/discovery](https://github.com/withastro/astro-discovery)*
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Basic site information
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
llms: {
|
||||
description: 'Documentation site for our API',
|
||||
keyFeatures: [
|
||||
'Interactive API explorer',
|
||||
'Code examples in multiple languages',
|
||||
'Live playground'
|
||||
]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### With content collections
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
llms: {
|
||||
importantPages: async () => {
|
||||
const [docs, guides] = await Promise.all([
|
||||
getCollection('docs'),
|
||||
getCollection('guides')
|
||||
]);
|
||||
|
||||
return [
|
||||
...docs.map(d => ({
|
||||
name: d.data.title,
|
||||
path: `/docs/${d.slug}`,
|
||||
description: d.data.description
|
||||
})),
|
||||
...guides.map(g => ({
|
||||
name: g.data.title,
|
||||
path: `/guides/${g.slug}`
|
||||
}))
|
||||
];
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Full API documentation
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
llms: {
|
||||
apiEndpoints: [
|
||||
{ path: '/api/users', method: 'GET', description: 'List users' },
|
||||
{ path: '/api/users', method: 'POST', description: 'Create user' },
|
||||
{ path: '/api/users/:id', method: 'GET', description: 'Get user' },
|
||||
{ path: '/api/users/:id', method: 'PUT', description: 'Update user' },
|
||||
{ path: '/api/users/:id', method: 'DELETE', description: 'Delete user' }
|
||||
]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Output Location
|
||||
|
||||
- **File:** `/llms.txt`
|
||||
- **URL:** `https://example.com/llms.txt`
|
||||
- **Cache-Control:** `public, max-age=3600` (1 hour, configurable via [caching](/reference/cache/))
|
||||
|
||||
@ -1,31 +1,291 @@
|
||||
---
|
||||
title: robots.txt Options
|
||||
description: Configuration reference for robots.txt
|
||||
title: robots.txt Configuration
|
||||
description: Configuration reference for robots.txt generation
|
||||
---
|
||||
|
||||
Detailed reference for all robots.txt configuration options and behaviors.
|
||||
Configuration reference for `/robots.txt` generation.
|
||||
|
||||
:::note[Work in Progress]
|
||||
This page is currently being developed. Check back soon for complete documentation.
|
||||
:::
|
||||
## RobotsConfig
|
||||
|
||||
## Coming Soon
|
||||
```typescript
|
||||
interface RobotsConfig {
|
||||
enabled?: boolean;
|
||||
crawlDelay?: number;
|
||||
allowAllBots?: boolean;
|
||||
llmBots?: {
|
||||
enabled?: boolean;
|
||||
agents?: string[];
|
||||
};
|
||||
additionalAgents?: Array<{
|
||||
userAgent: string;
|
||||
allow?: string[];
|
||||
disallow?: string[];
|
||||
}>;
|
||||
customRules?: string;
|
||||
}
|
||||
```
|
||||
|
||||
This section will include:
|
||||
- Detailed explanations
|
||||
- Code examples
|
||||
- Best practices
|
||||
- Common patterns
|
||||
- Troubleshooting tips
|
||||
## Properties
|
||||
|
||||
## Related Pages
|
||||
### enabled
|
||||
|
||||
- [Configuration Reference](/reference/configuration/)
|
||||
- [API Reference](/reference/api/)
|
||||
- [Examples](/examples/ecommerce/)
|
||||
- **Type:** `boolean`
|
||||
- **Default:** `true`
|
||||
- **Description:** Enable or disable robots.txt generation
|
||||
|
||||
## Need Help?
|
||||
```typescript
|
||||
discovery({
|
||||
robots: {
|
||||
enabled: false // Disable robots.txt
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
- Check our [FAQ](/community/faq/)
|
||||
- Visit [Troubleshooting](/community/troubleshooting/)
|
||||
- Open an issue on [GitHub](https://github.com/withastro/astro-discovery/issues)
|
||||
### crawlDelay
|
||||
|
||||
- **Type:** `number`
|
||||
- **Default:** `1`
|
||||
- **Description:** Crawl delay in seconds for polite crawlers
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
robots: {
|
||||
crawlDelay: 2 // Wait 2 seconds between requests
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### allowAllBots
|
||||
|
||||
- **Type:** `boolean`
|
||||
- **Default:** `true`
|
||||
- **Description:** Include `User-agent: *` with `Allow: /` directive
|
||||
|
||||
**When `true` (default):**
|
||||
```
|
||||
User-agent: *
|
||||
Allow: /
|
||||
```
|
||||
|
||||
**When `false`:**
|
||||
```
|
||||
# No default allow rule
|
||||
```
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
robots: {
|
||||
allowAllBots: false // No default allow
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### llmBots
|
||||
|
||||
LLM-specific bot configuration.
|
||||
|
||||
#### llmBots.enabled
|
||||
|
||||
- **Type:** `boolean`
|
||||
- **Default:** `true`
|
||||
- **Description:** Include LLM bot section referencing `/llms.txt`
|
||||
|
||||
#### llmBots.agents
|
||||
|
||||
- **Type:** `string[]`
|
||||
- **Default:**
|
||||
```typescript
|
||||
[
|
||||
'Anthropic-AI',
|
||||
'Claude-Web',
|
||||
'GPTBot',
|
||||
'ChatGPT-User',
|
||||
'cohere-ai',
|
||||
'Google-Extended',
|
||||
'PerplexityBot',
|
||||
'Applebot-Extended'
|
||||
]
|
||||
```
|
||||
- **Description:** LLM bot user agents to list
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
discovery({
|
||||
robots: {
|
||||
llmBots: {
|
||||
enabled: true,
|
||||
agents: ['CustomAI', 'AnotherBot']
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**Generated output:**
|
||||
```
|
||||
# LLM-specific resources
|
||||
# AI assistants can find additional context at /llms.txt
|
||||
# See: https://github.com/anthropics/llm-txt
|
||||
|
||||
User-agent: CustomAI
|
||||
User-agent: AnotherBot
|
||||
Allow: /llms.txt
|
||||
Allow: /llms-full.txt
|
||||
```
|
||||
|
||||
### additionalAgents
|
||||
|
||||
- **Type:** `Array<{ userAgent: string; allow?: string[]; disallow?: string[] }>`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Custom agent-specific rules
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
discovery({
|
||||
robots: {
|
||||
additionalAgents: [
|
||||
{
|
||||
userAgent: 'BadBot',
|
||||
disallow: ['/']
|
||||
},
|
||||
{
|
||||
userAgent: 'GoodBot',
|
||||
allow: ['/api/public'],
|
||||
disallow: ['/api/private', '/admin']
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**Generated output:**
|
||||
```
|
||||
# Custom agent rules
|
||||
|
||||
User-agent: BadBot
|
||||
Disallow: /
|
||||
|
||||
User-agent: GoodBot
|
||||
Allow: /api/public
|
||||
Disallow: /api/private
|
||||
Disallow: /admin
|
||||
```
|
||||
|
||||
### customRules
|
||||
|
||||
- **Type:** `string`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Raw robots.txt content appended to end of file
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
discovery({
|
||||
robots: {
|
||||
customRules: `
|
||||
# Custom section
|
||||
User-agent: SpecialBot
|
||||
Crawl-delay: 10
|
||||
Request-rate: 1/5
|
||||
`.trim()
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Default Output
|
||||
|
||||
With default configuration:
|
||||
|
||||
```
|
||||
# robots.txt
|
||||
# Generated by @astrojs/discovery for example.com
|
||||
|
||||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
# Sitemaps
|
||||
Sitemap: https://example.com/sitemap-index.xml
|
||||
|
||||
# LLM-specific resources
|
||||
# AI assistants can find additional context at /llms.txt
|
||||
# See: https://github.com/anthropics/llm-txt
|
||||
|
||||
User-agent: Anthropic-AI
|
||||
User-agent: Claude-Web
|
||||
User-agent: GPTBot
|
||||
User-agent: ChatGPT-User
|
||||
User-agent: cohere-ai
|
||||
User-agent: Google-Extended
|
||||
User-agent: PerplexityBot
|
||||
User-agent: Applebot-Extended
|
||||
Allow: /llms.txt
|
||||
Allow: /llms-full.txt
|
||||
|
||||
# Crawl delay (be nice to our server)
|
||||
Crawl-delay: 1
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Block all bots from admin area
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
robots: {
|
||||
additionalAgents: [
|
||||
{
|
||||
userAgent: '*',
|
||||
disallow: ['/admin', '/api/private']
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Disable LLM bot access
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
robots: {
|
||||
llmBots: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Custom LLM bot list
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
robots: {
|
||||
llmBots: {
|
||||
enabled: true,
|
||||
agents: [
|
||||
'Anthropic-AI',
|
||||
'Claude-Web',
|
||||
'MyCustomBot'
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Block specific bad bot
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
robots: {
|
||||
additionalAgents: [
|
||||
{
|
||||
userAgent: 'BadBot',
|
||||
disallow: ['/']
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Output Location
|
||||
|
||||
- **File:** `/robots.txt`
|
||||
- **URL:** `https://example.com/robots.txt`
|
||||
- **Cache-Control:** `public, max-age=3600` (1 hour, configurable via [caching](/reference/cache/))
|
||||
|
||||
@ -1,31 +1,334 @@
|
||||
---
|
||||
title: security.txt Options
|
||||
title: security.txt Configuration
|
||||
description: Configuration reference for security.txt (RFC 9116)
|
||||
---
|
||||
|
||||
RFC 9116 compliant security.txt configuration reference.
|
||||
Configuration reference for `/.well-known/security.txt` generation (RFC 9116).
|
||||
|
||||
:::note[Work in Progress]
|
||||
This page is currently being developed. Check back soon for complete documentation.
|
||||
:::
|
||||
## SecurityConfig
|
||||
|
||||
## Coming Soon
|
||||
```typescript
|
||||
interface SecurityConfig {
|
||||
enabled?: boolean;
|
||||
contact: string | string[]; // REQUIRED
|
||||
expires?: string | 'auto';
|
||||
encryption?: string | string[];
|
||||
acknowledgments?: string;
|
||||
preferredLanguages?: string[];
|
||||
canonical?: string;
|
||||
policy?: string;
|
||||
hiring?: string;
|
||||
}
|
||||
```
|
||||
|
||||
This section will include:
|
||||
- Detailed explanations
|
||||
- Code examples
|
||||
- Best practices
|
||||
- Common patterns
|
||||
- Troubleshooting tips
|
||||
## Properties
|
||||
|
||||
## Related Pages
|
||||
### enabled
|
||||
|
||||
- [Configuration Reference](/reference/configuration/)
|
||||
- [API Reference](/reference/api/)
|
||||
- [Examples](/examples/ecommerce/)
|
||||
- **Type:** `boolean`
|
||||
- **Default:** `true`
|
||||
- **Description:** Enable or disable security.txt generation
|
||||
|
||||
## Need Help?
|
||||
### contact (Required)
|
||||
|
||||
- Check our [FAQ](/community/faq/)
|
||||
- Visit [Troubleshooting](/community/troubleshooting/)
|
||||
- Open an issue on [GitHub](https://github.com/withastro/astro-discovery/issues)
|
||||
- **Type:** `string | string[]`
|
||||
- **Required:** **Yes**
|
||||
- **Description:** Contact email or URL for security issues
|
||||
- **RFC Requirement:** Required field per RFC 9116
|
||||
|
||||
**Email example:**
|
||||
```typescript
|
||||
discovery({
|
||||
security: {
|
||||
contact: 'security@example.com' // Auto-converts to mailto:
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**Multiple contacts:**
|
||||
```typescript
|
||||
discovery({
|
||||
security: {
|
||||
contact: [
|
||||
'security@example.com',
|
||||
'https://example.com/security/report'
|
||||
]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**Generated output:**
|
||||
```
|
||||
Contact: mailto:security@example.com
|
||||
Contact: https://example.com/security/report
|
||||
```
|
||||
|
||||
### expires
|
||||
|
||||
- **Type:** `string | 'auto'`
|
||||
- **Default:** `'auto'` (1 year from generation)
|
||||
- **Description:** Expiration date in ISO 8601 format
|
||||
- **RFC Requirement:** Required field per RFC 9116
|
||||
|
||||
**Auto-expiration (recommended):**
|
||||
```typescript
|
||||
discovery({
|
||||
security: {
|
||||
contact: 'security@example.com',
|
||||
expires: 'auto' // Sets to 1 year from build date
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**Manual expiration:**
|
||||
```typescript
|
||||
discovery({
|
||||
security: {
|
||||
contact: 'security@example.com',
|
||||
expires: '2026-12-31T23:59:59Z'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### encryption
|
||||
|
||||
- **Type:** `string | string[]`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** URL to encryption key (PGP public key)
|
||||
|
||||
**Single key:**
|
||||
```typescript
|
||||
discovery({
|
||||
security: {
|
||||
contact: 'security@example.com',
|
||||
encryption: 'https://example.com/pgp-key.txt'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**Multiple keys:**
|
||||
```typescript
|
||||
discovery({
|
||||
security: {
|
||||
contact: 'security@example.com',
|
||||
encryption: [
|
||||
'https://example.com/pgp-key.txt',
|
||||
'https://keys.openpgp.org/vks/v1/by-fingerprint/ABC123'
|
||||
]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### acknowledgments
|
||||
|
||||
- **Type:** `string`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** URL to security acknowledgments/hall of fame page
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
discovery({
|
||||
security: {
|
||||
contact: 'security@example.com',
|
||||
acknowledgments: 'https://example.com/security/hall-of-fame'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### preferredLanguages
|
||||
|
||||
- **Type:** `string[]`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Preferred languages for security reports (ISO 639-1 codes)
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
discovery({
|
||||
security: {
|
||||
contact: 'security@example.com',
|
||||
preferredLanguages: ['en', 'es', 'fr']
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**Generated output:**
|
||||
```
|
||||
Preferred-Languages: en, es, fr
|
||||
```
|
||||
|
||||
### canonical
|
||||
|
||||
- **Type:** `string`
|
||||
- **Default:** `https://{site}/.well-known/security.txt`
|
||||
- **Description:** Canonical URL for security.txt location
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
discovery({
|
||||
security: {
|
||||
contact: 'security@example.com',
|
||||
canonical: 'https://example.com/.well-known/security.txt'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### policy
|
||||
|
||||
- **Type:** `string`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** URL to security policy or disclosure policy
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
discovery({
|
||||
security: {
|
||||
contact: 'security@example.com',
|
||||
policy: 'https://example.com/security/disclosure-policy'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### hiring
|
||||
|
||||
- **Type:** `string`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** URL for security job postings
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
discovery({
|
||||
security: {
|
||||
contact: 'security@example.com',
|
||||
hiring: 'https://example.com/careers/security'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Generated Output
|
||||
|
||||
**Minimal configuration:**
|
||||
```typescript
|
||||
discovery({
|
||||
security: {
|
||||
contact: 'security@example.com'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
Canonical: https://example.com/.well-known/security.txt
|
||||
|
||||
Contact: mailto:security@example.com
|
||||
|
||||
Expires: 2026-11-08T00:00:00.000Z
|
||||
```
|
||||
|
||||
**Full configuration:**
|
||||
```typescript
|
||||
discovery({
|
||||
security: {
|
||||
contact: [
|
||||
'security@example.com',
|
||||
'https://example.com/security/report'
|
||||
],
|
||||
expires: 'auto',
|
||||
encryption: 'https://example.com/pgp-key.txt',
|
||||
acknowledgments: 'https://example.com/security/hall-of-fame',
|
||||
preferredLanguages: ['en', 'es'],
|
||||
policy: 'https://example.com/security/policy',
|
||||
hiring: 'https://example.com/careers/security'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
Canonical: https://example.com/.well-known/security.txt
|
||||
|
||||
Contact: mailto:security@example.com
|
||||
Contact: https://example.com/security/report
|
||||
|
||||
Expires: 2026-11-08T00:00:00.000Z
|
||||
|
||||
Encryption: https://example.com/pgp-key.txt
|
||||
|
||||
Acknowledgments: https://example.com/security/hall-of-fame
|
||||
|
||||
Preferred-Languages: en, es
|
||||
|
||||
Policy: https://example.com/security/policy
|
||||
|
||||
Hiring: https://example.com/careers/security
|
||||
```
|
||||
|
||||
## RFC 9116 Compliance
|
||||
|
||||
This implementation follows RFC 9116 requirements:
|
||||
|
||||
1. **Required fields:** `Contact` and `Expires` are mandatory
|
||||
2. **Location:** Served at `/.well-known/security.txt`
|
||||
3. **Format:** Plain text with field-value pairs
|
||||
4. **Email handling:** Automatically adds `mailto:` prefix
|
||||
5. **Canonical URL:** Defaults to correct `.well-known` location
|
||||
6. **Field ordering:** Canonical first, then required fields
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Minimal setup
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
security: {
|
||||
contact: 'security@example.com'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### With PGP encryption
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
security: {
|
||||
contact: 'security@example.com',
|
||||
encryption: 'https://example.com/pgp-key.txt',
|
||||
preferredLanguages: ['en']
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Full security program
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
security: {
|
||||
contact: [
|
||||
'security@example.com',
|
||||
'https://hackerone.com/example'
|
||||
],
|
||||
expires: 'auto',
|
||||
encryption: 'https://example.com/security.asc',
|
||||
acknowledgments: 'https://example.com/security/thanks',
|
||||
preferredLanguages: ['en', 'es', 'fr'],
|
||||
policy: 'https://example.com/security/disclosure',
|
||||
hiring: 'https://example.com/careers/security-engineer'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always set contact:** This is required by RFC 9116
|
||||
2. **Use auto-expiration:** Let the integration manage expiration dates
|
||||
3. **Provide encryption:** Offer PGP keys for secure communication
|
||||
4. **Multiple contact methods:** Email + bug bounty platform
|
||||
5. **Acknowledge researchers:** Link to your hall of fame
|
||||
6. **Document policy:** Clear disclosure timelines and expectations
|
||||
7. **Monitor expiration:** Security.txt should never expire
|
||||
|
||||
## Output Location
|
||||
|
||||
- **File:** `/.well-known/security.txt`
|
||||
- **URL:** `https://example.com/.well-known/security.txt`
|
||||
- **Cache-Control:** `public, max-age=86400` (24 hours, configurable via [caching](/reference/cache/))
|
||||
- **RFC:** [RFC 9116](https://datatracker.ietf.org/doc/html/rfc9116)
|
||||
|
||||
@ -1,31 +1,230 @@
|
||||
---
|
||||
title: Sitemap Options
|
||||
title: Sitemap Configuration
|
||||
description: Configuration reference for sitemap generation
|
||||
---
|
||||
|
||||
Reference for sitemap configuration options (passed to @astrojs/sitemap).
|
||||
Configuration reference for sitemap generation (passed to `@astrojs/sitemap`).
|
||||
|
||||
:::note[Work in Progress]
|
||||
This page is currently being developed. Check back soon for complete documentation.
|
||||
:::
|
||||
## SitemapConfig
|
||||
|
||||
## Coming Soon
|
||||
```typescript
|
||||
interface SitemapConfig {
|
||||
filter?: (page: string) => boolean;
|
||||
customPages?: string[];
|
||||
changefreq?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never';
|
||||
priority?: number;
|
||||
i18n?: {
|
||||
defaultLocale: string;
|
||||
locales: Record<string, string>;
|
||||
};
|
||||
lastmod?: Date;
|
||||
serialize?: (item: SitemapItem) => SitemapItem | undefined;
|
||||
[key: string]: any;
|
||||
}
|
||||
```
|
||||
|
||||
This section will include:
|
||||
- Detailed explanations
|
||||
- Code examples
|
||||
- Best practices
|
||||
- Common patterns
|
||||
- Troubleshooting tips
|
||||
## Properties
|
||||
|
||||
## Related Pages
|
||||
All options are passed directly to `@astrojs/sitemap`. See [Astro Sitemap documentation](https://docs.astro.build/en/guides/integrations-guide/sitemap/) for complete details.
|
||||
|
||||
- [Configuration Reference](/reference/configuration/)
|
||||
- [API Reference](/reference/api/)
|
||||
- [Examples](/examples/ecommerce/)
|
||||
### filter
|
||||
|
||||
## Need Help?
|
||||
- **Type:** `(page: string) => boolean`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Filter function to exclude pages
|
||||
|
||||
- Check our [FAQ](/community/faq/)
|
||||
- Visit [Troubleshooting](/community/troubleshooting/)
|
||||
- Open an issue on [GitHub](https://github.com/withastro/astro-discovery/issues)
|
||||
**Example:**
|
||||
```typescript
|
||||
discovery({
|
||||
sitemap: {
|
||||
filter: (page) => {
|
||||
return !page.includes('/admin') &&
|
||||
!page.includes('/draft') &&
|
||||
!page.includes('/private');
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### customPages
|
||||
|
||||
- **Type:** `string[]`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Custom pages to include in sitemap
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
discovery({
|
||||
sitemap: {
|
||||
customPages: [
|
||||
'https://example.com/external-page',
|
||||
'https://example.com/another-page'
|
||||
]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### changefreq
|
||||
|
||||
- **Type:** `'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never'`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Change frequency hint for search engines
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
discovery({
|
||||
sitemap: {
|
||||
changefreq: 'weekly'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### priority
|
||||
|
||||
- **Type:** `number` (0.0 - 1.0)
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Priority hint for search engines
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
discovery({
|
||||
sitemap: {
|
||||
priority: 0.8
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### i18n
|
||||
|
||||
- **Type:** `{ defaultLocale: string; locales: Record<string, string> }`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Internationalization configuration
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
discovery({
|
||||
sitemap: {
|
||||
i18n: {
|
||||
defaultLocale: 'en',
|
||||
locales: {
|
||||
en: 'en-US',
|
||||
es: 'es-ES',
|
||||
fr: 'fr-FR'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### lastmod
|
||||
|
||||
- **Type:** `Date`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Last modification date for all pages
|
||||
|
||||
### serialize
|
||||
|
||||
- **Type:** `(item: SitemapItem) => SitemapItem | undefined`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Custom serialization function
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
discovery({
|
||||
sitemap: {
|
||||
serialize: (item) => {
|
||||
// Skip draft pages
|
||||
if (item.url.includes('/draft')) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Set higher priority for docs
|
||||
if (item.url.includes('/docs')) {
|
||||
item.priority = 0.9;
|
||||
item.changefreq = 'daily';
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Exclude admin and private pages
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
sitemap: {
|
||||
filter: (page) =>
|
||||
!page.includes('/admin') &&
|
||||
!page.includes('/private') &&
|
||||
!page.includes('/_')
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Set change frequency
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
sitemap: {
|
||||
changefreq: 'daily',
|
||||
priority: 0.7
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Multilingual sitemap
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
sitemap: {
|
||||
i18n: {
|
||||
defaultLocale: 'en',
|
||||
locales: {
|
||||
en: 'en-US',
|
||||
es: 'es-ES',
|
||||
fr: 'fr-FR',
|
||||
de: 'de-DE'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Custom page priorities
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
sitemap: {
|
||||
serialize: (item) => {
|
||||
if (item.url.includes('/docs')) {
|
||||
item.priority = 0.9;
|
||||
item.changefreq = 'weekly';
|
||||
} else if (item.url.includes('/blog')) {
|
||||
item.priority = 0.7;
|
||||
item.changefreq = 'daily';
|
||||
} else {
|
||||
item.priority = 0.5;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Output Location
|
||||
|
||||
- **File:** `/sitemap-index.xml` (or `/sitemap-0.xml` if small)
|
||||
- **URL:** `https://example.com/sitemap-index.xml`
|
||||
- **Cache-Control:** `public, max-age=3600` (1 hour, configurable via [caching](/reference/cache/))
|
||||
- **Documentation:** [Astro Sitemap Guide](https://docs.astro.build/en/guides/integrations-guide/sitemap/)
|
||||
|
||||
## Notes
|
||||
|
||||
- Automatically included by `@astrojs/discovery`
|
||||
- No need to install `@astrojs/sitemap` separately
|
||||
- Referenced in `robots.txt` automatically
|
||||
- Supports both static and server-rendered pages
|
||||
|
||||
@ -1,31 +1,364 @@
|
||||
---
|
||||
title: TypeScript Types
|
||||
description: TypeScript type definitions and interfaces
|
||||
description: Complete TypeScript type definitions
|
||||
---
|
||||
|
||||
Complete TypeScript type reference for configuration and APIs.
|
||||
Complete TypeScript type reference for `@astrojs/discovery`.
|
||||
|
||||
:::note[Work in Progress]
|
||||
This page is currently being developed. Check back soon for complete documentation.
|
||||
:::
|
||||
## Import Types
|
||||
|
||||
## Coming Soon
|
||||
```typescript
|
||||
import type {
|
||||
DiscoveryConfig,
|
||||
RobotsConfig,
|
||||
LLMsConfig,
|
||||
HumansConfig,
|
||||
SecurityConfig,
|
||||
CanaryConfig,
|
||||
WebFingerConfig,
|
||||
SitemapConfig,
|
||||
CachingConfig,
|
||||
TemplateConfig,
|
||||
ImportantPage,
|
||||
APIEndpoint,
|
||||
TechStack,
|
||||
TeamMember,
|
||||
SiteInfo,
|
||||
CanaryStatement,
|
||||
WebFingerResource,
|
||||
WebFingerLink
|
||||
} from '@astrojs/discovery';
|
||||
```
|
||||
|
||||
This section will include:
|
||||
- Detailed explanations
|
||||
- Code examples
|
||||
- Best practices
|
||||
- Common patterns
|
||||
- Troubleshooting tips
|
||||
## Main Configuration
|
||||
|
||||
## Related Pages
|
||||
```typescript
|
||||
interface DiscoveryConfig {
|
||||
robots?: RobotsConfig;
|
||||
llms?: LLMsConfig;
|
||||
humans?: HumansConfig;
|
||||
security?: SecurityConfig;
|
||||
canary?: CanaryConfig;
|
||||
webfinger?: WebFingerConfig;
|
||||
sitemap?: SitemapConfig;
|
||||
caching?: CachingConfig;
|
||||
templates?: TemplateConfig;
|
||||
}
|
||||
```
|
||||
|
||||
- [Configuration Reference](/reference/configuration/)
|
||||
- [API Reference](/reference/api/)
|
||||
- [Examples](/examples/ecommerce/)
|
||||
## File Configuration Types
|
||||
|
||||
## Need Help?
|
||||
### RobotsConfig
|
||||
|
||||
- Check our [FAQ](/community/faq/)
|
||||
- Visit [Troubleshooting](/community/troubleshooting/)
|
||||
- Open an issue on [GitHub](https://github.com/withastro/astro-discovery/issues)
|
||||
```typescript
|
||||
interface RobotsConfig {
|
||||
enabled?: boolean;
|
||||
crawlDelay?: number;
|
||||
allowAllBots?: boolean;
|
||||
llmBots?: {
|
||||
enabled?: boolean;
|
||||
agents?: string[];
|
||||
};
|
||||
additionalAgents?: Array<{
|
||||
userAgent: string;
|
||||
allow?: string[];
|
||||
disallow?: string[];
|
||||
}>;
|
||||
customRules?: string;
|
||||
}
|
||||
```
|
||||
|
||||
### LLMsConfig
|
||||
|
||||
```typescript
|
||||
interface LLMsConfig {
|
||||
enabled?: boolean;
|
||||
description?: string | (() => string);
|
||||
keyFeatures?: string[];
|
||||
importantPages?: ImportantPage[] | (() => Promise<ImportantPage[]>);
|
||||
instructions?: string;
|
||||
apiEndpoints?: APIEndpoint[];
|
||||
techStack?: TechStack;
|
||||
brandVoice?: string[];
|
||||
customSections?: Record<string, string>;
|
||||
}
|
||||
```
|
||||
|
||||
### HumansConfig
|
||||
|
||||
```typescript
|
||||
interface HumansConfig {
|
||||
enabled?: boolean;
|
||||
team?: TeamMember[];
|
||||
thanks?: string[];
|
||||
site?: SiteInfo;
|
||||
story?: string;
|
||||
funFacts?: string[];
|
||||
philosophy?: string[];
|
||||
customSections?: Record<string, string>;
|
||||
}
|
||||
```
|
||||
|
||||
### SecurityConfig
|
||||
|
||||
```typescript
|
||||
interface SecurityConfig {
|
||||
enabled?: boolean;
|
||||
contact: string | string[];
|
||||
expires?: string | 'auto';
|
||||
encryption?: string | string[];
|
||||
acknowledgments?: string;
|
||||
preferredLanguages?: string[];
|
||||
canonical?: string;
|
||||
policy?: string;
|
||||
hiring?: string;
|
||||
}
|
||||
```
|
||||
|
||||
### CanaryConfig
|
||||
|
||||
```typescript
|
||||
interface CanaryConfig {
|
||||
enabled?: boolean;
|
||||
organization?: string;
|
||||
contact?: string;
|
||||
frequency?: 'daily' | 'weekly' | 'monthly' | 'quarterly' | 'yearly';
|
||||
expires?: string | 'auto';
|
||||
statements?: CanaryStatement[] | (() => CanaryStatement[]);
|
||||
additionalStatement?: string;
|
||||
verification?: string;
|
||||
previousCanary?: string;
|
||||
blockchainProof?: {
|
||||
network: string;
|
||||
address: string;
|
||||
txHash?: string;
|
||||
timestamp?: string;
|
||||
};
|
||||
personnelStatement?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
### WebFingerConfig
|
||||
|
||||
```typescript
|
||||
interface WebFingerConfig {
|
||||
enabled?: boolean;
|
||||
resources?: WebFingerResource[];
|
||||
collections?: {
|
||||
name: string;
|
||||
resourceTemplate: string;
|
||||
subjectTemplate?: string;
|
||||
linksBuilder?: (entry: any) => WebFingerLink[];
|
||||
aliasesBuilder?: (entry: any) => string[];
|
||||
propertiesBuilder?: (entry: any) => Record<string, string | null>;
|
||||
}[];
|
||||
}
|
||||
```
|
||||
|
||||
### SitemapConfig
|
||||
|
||||
```typescript
|
||||
interface SitemapConfig {
|
||||
filter?: (page: string) => boolean;
|
||||
customPages?: string[];
|
||||
changefreq?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never';
|
||||
priority?: number;
|
||||
i18n?: {
|
||||
defaultLocale: string;
|
||||
locales: Record<string, string>;
|
||||
};
|
||||
lastmod?: Date;
|
||||
[key: string]: any;
|
||||
}
|
||||
```
|
||||
|
||||
### CachingConfig
|
||||
|
||||
```typescript
|
||||
interface CachingConfig {
|
||||
robots?: number;
|
||||
llms?: number;
|
||||
humans?: number;
|
||||
security?: number;
|
||||
canary?: number;
|
||||
webfinger?: number;
|
||||
sitemap?: number;
|
||||
}
|
||||
```
|
||||
|
||||
### TemplateConfig
|
||||
|
||||
```typescript
|
||||
interface TemplateConfig {
|
||||
robots?: (config: RobotsConfig, siteURL: URL) => string;
|
||||
llms?: (config: LLMsConfig, siteURL: URL) => string | Promise<string>;
|
||||
humans?: (config: HumansConfig, siteURL: URL) => string;
|
||||
security?: (config: SecurityConfig, siteURL: URL) => string;
|
||||
canary?: (config: CanaryConfig, siteURL: URL) => string;
|
||||
}
|
||||
```
|
||||
|
||||
## Supporting Types
|
||||
|
||||
### ImportantPage
|
||||
|
||||
```typescript
|
||||
interface ImportantPage {
|
||||
name: string;
|
||||
path: string;
|
||||
description?: string;
|
||||
}
|
||||
```
|
||||
|
||||
### APIEndpoint
|
||||
|
||||
```typescript
|
||||
interface APIEndpoint {
|
||||
path: string;
|
||||
method?: string;
|
||||
description: string;
|
||||
}
|
||||
```
|
||||
|
||||
### TechStack
|
||||
|
||||
```typescript
|
||||
interface TechStack {
|
||||
frontend?: string[];
|
||||
backend?: string[];
|
||||
ai?: string[];
|
||||
other?: string[];
|
||||
}
|
||||
```
|
||||
|
||||
### TeamMember
|
||||
|
||||
```typescript
|
||||
interface TeamMember {
|
||||
name: string;
|
||||
role?: string;
|
||||
contact?: string;
|
||||
location?: string;
|
||||
twitter?: string;
|
||||
github?: string;
|
||||
}
|
||||
```
|
||||
|
||||
### SiteInfo
|
||||
|
||||
```typescript
|
||||
interface SiteInfo {
|
||||
lastUpdate?: string | 'auto';
|
||||
language?: string;
|
||||
doctype?: string;
|
||||
ide?: string;
|
||||
techStack?: string[];
|
||||
standards?: string[];
|
||||
components?: string[];
|
||||
software?: string[];
|
||||
}
|
||||
```
|
||||
|
||||
### CanaryStatement
|
||||
|
||||
```typescript
|
||||
interface CanaryStatement {
|
||||
type: 'nsl' | 'fisa' | 'gag' | 'surveillance' | 'backdoor' | 'encryption' | 'other';
|
||||
description: string;
|
||||
received: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
### WebFingerResource
|
||||
|
||||
```typescript
|
||||
interface WebFingerResource {
|
||||
resource: string;
|
||||
subject?: string;
|
||||
aliases?: string[];
|
||||
properties?: Record<string, string | null>;
|
||||
links?: WebFingerLink[];
|
||||
}
|
||||
```
|
||||
|
||||
### WebFingerLink
|
||||
|
||||
```typescript
|
||||
interface WebFingerLink {
|
||||
rel: string;
|
||||
href?: string;
|
||||
type?: string;
|
||||
titles?: Record<string, string>;
|
||||
properties?: Record<string, string | null>;
|
||||
}
|
||||
```
|
||||
|
||||
### SitemapItem
|
||||
|
||||
```typescript
|
||||
interface SitemapItem {
|
||||
url: string;
|
||||
lastmod?: Date;
|
||||
changefreq?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never';
|
||||
priority?: number;
|
||||
links?: Array<{
|
||||
url: string;
|
||||
lang: string;
|
||||
}>;
|
||||
}
|
||||
```
|
||||
|
||||
## Type Guards
|
||||
|
||||
### Check if config has security
|
||||
|
||||
```typescript
|
||||
function hasSecurityConfig(config: DiscoveryConfig): config is Required<Pick<DiscoveryConfig, 'security'>> & DiscoveryConfig {
|
||||
return config.security !== undefined && config.security.contact !== undefined;
|
||||
}
|
||||
```
|
||||
|
||||
### Check if config has canary
|
||||
|
||||
```typescript
|
||||
function hasCanaryConfig(config: DiscoveryConfig): config is Required<Pick<DiscoveryConfig, 'canary'>> & DiscoveryConfig {
|
||||
return config.canary !== undefined;
|
||||
}
|
||||
```
|
||||
|
||||
## Example Usage
|
||||
|
||||
```typescript
|
||||
import { defineConfig } from 'astro/config';
|
||||
import discovery from '@astrojs/discovery';
|
||||
import type { DiscoveryConfig, LLMsConfig } from '@astrojs/discovery';
|
||||
|
||||
const llmsConfig: LLMsConfig = {
|
||||
description: 'My awesome site',
|
||||
keyFeatures: ['Feature 1', 'Feature 2']
|
||||
};
|
||||
|
||||
const discoveryConfig: DiscoveryConfig = {
|
||||
llms: llmsConfig,
|
||||
robots: {
|
||||
crawlDelay: 2
|
||||
},
|
||||
caching: {
|
||||
llms: 1800
|
||||
}
|
||||
};
|
||||
|
||||
export default defineConfig({
|
||||
site: 'https://example.com',
|
||||
integrations: [
|
||||
discovery(discoveryConfig)
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- All configuration interfaces are exported from `@astrojs/discovery`
|
||||
- All properties are optional unless marked "Required"
|
||||
- Use TypeScript's type inference for better DX
|
||||
- Enable strict mode for better type safety
|
||||
|
||||
@ -1,31 +1,375 @@
|
||||
---
|
||||
title: WebFinger Options
|
||||
title: WebFinger Configuration
|
||||
description: Configuration reference for WebFinger (RFC 7033)
|
||||
---
|
||||
|
||||
RFC 7033 compliant WebFinger configuration reference.
|
||||
Configuration reference for `/.well-known/webfinger` resource discovery (RFC 7033).
|
||||
|
||||
:::note[Work in Progress]
|
||||
This page is currently being developed. Check back soon for complete documentation.
|
||||
:::
|
||||
## WebFingerConfig
|
||||
|
||||
## Coming Soon
|
||||
```typescript
|
||||
interface WebFingerConfig {
|
||||
enabled?: boolean;
|
||||
resources?: WebFingerResource[];
|
||||
collections?: {
|
||||
name: string;
|
||||
resourceTemplate: string;
|
||||
subjectTemplate?: string;
|
||||
linksBuilder?: (entry: any) => WebFingerLink[];
|
||||
aliasesBuilder?: (entry: any) => string[];
|
||||
propertiesBuilder?: (entry: any) => Record<string, string | null>;
|
||||
}[];
|
||||
}
|
||||
```
|
||||
|
||||
This section will include:
|
||||
- Detailed explanations
|
||||
- Code examples
|
||||
- Best practices
|
||||
- Common patterns
|
||||
- Troubleshooting tips
|
||||
## Properties
|
||||
|
||||
## Related Pages
|
||||
### enabled
|
||||
|
||||
- [Configuration Reference](/reference/configuration/)
|
||||
- [API Reference](/reference/api/)
|
||||
- [Examples](/examples/ecommerce/)
|
||||
- **Type:** `boolean`
|
||||
- **Default:** `false` (opt-in)
|
||||
- **Description:** Enable or disable WebFinger generation
|
||||
|
||||
## Need Help?
|
||||
**Note:** WebFinger is opt-in by default because it requires configuration.
|
||||
|
||||
- Check our [FAQ](/community/faq/)
|
||||
- Visit [Troubleshooting](/community/troubleshooting/)
|
||||
- Open an issue on [GitHub](https://github.com/withastro/astro-discovery/issues)
|
||||
### resources
|
||||
|
||||
- **Type:** `WebFingerResource[]`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Static resources to expose via WebFinger
|
||||
|
||||
**WebFingerResource interface:**
|
||||
```typescript
|
||||
interface WebFingerResource {
|
||||
resource: string;
|
||||
subject?: string;
|
||||
aliases?: string[];
|
||||
properties?: Record<string, string | null>;
|
||||
links?: WebFingerLink[];
|
||||
}
|
||||
```
|
||||
|
||||
**WebFingerLink interface:**
|
||||
```typescript
|
||||
interface WebFingerLink {
|
||||
rel: string;
|
||||
href?: string;
|
||||
type?: string;
|
||||
titles?: Record<string, string>;
|
||||
properties?: Record<string, string | null>;
|
||||
}
|
||||
```
|
||||
|
||||
**Static resource example:**
|
||||
```typescript
|
||||
discovery({
|
||||
webfinger: {
|
||||
enabled: true,
|
||||
resources: [
|
||||
{
|
||||
resource: 'acct:alice@example.com',
|
||||
subject: 'acct:alice@example.com',
|
||||
aliases: [
|
||||
'https://example.com/@alice',
|
||||
'https://example.com/users/alice'
|
||||
],
|
||||
properties: {
|
||||
'http://schema.org/name': 'Alice Developer'
|
||||
},
|
||||
links: [
|
||||
{
|
||||
rel: 'http://webfinger.net/rel/profile-page',
|
||||
type: 'text/html',
|
||||
href: 'https://example.com/@alice'
|
||||
},
|
||||
{
|
||||
rel: 'self',
|
||||
type: 'application/activity+json',
|
||||
href: 'https://example.com/users/alice'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### collections
|
||||
|
||||
- **Type:** Collection configuration array
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Content collection integration for dynamic WebFinger resources
|
||||
|
||||
#### Collection Properties
|
||||
|
||||
##### name
|
||||
|
||||
- **Type:** `string`
|
||||
- **Required:** Yes
|
||||
- **Description:** Astro content collection name
|
||||
|
||||
##### resourceTemplate
|
||||
|
||||
- **Type:** `string`
|
||||
- **Required:** Yes
|
||||
- **Description:** Resource URI template with variables
|
||||
- **Supported variables:** `{slug}`, `{id}`, `{data.fieldName}`, `{siteURL}`
|
||||
|
||||
##### subjectTemplate
|
||||
|
||||
- **Type:** `string`
|
||||
- **Default:** Same as `resourceTemplate`
|
||||
- **Description:** Subject URI template
|
||||
|
||||
##### linksBuilder
|
||||
|
||||
- **Type:** `(entry: any) => WebFingerLink[]`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Function to generate links for a collection entry
|
||||
|
||||
##### aliasesBuilder
|
||||
|
||||
- **Type:** `(entry: any) => string[]`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Function to generate aliases for a collection entry
|
||||
|
||||
##### propertiesBuilder
|
||||
|
||||
- **Type:** `(entry: any) => Record<string, string | null>`
|
||||
- **Default:** `undefined`
|
||||
- **Description:** Function to generate properties for a collection entry
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### ActivityPub / Mastodon
|
||||
|
||||
Enable federated social network discovery:
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
webfinger: {
|
||||
enabled: true,
|
||||
collections: [{
|
||||
name: 'team',
|
||||
resourceTemplate: 'acct:{slug}@example.com',
|
||||
linksBuilder: (member) => [
|
||||
{
|
||||
rel: 'self',
|
||||
type: 'application/activity+json',
|
||||
href: `https://example.com/users/${member.slug}`
|
||||
},
|
||||
{
|
||||
rel: 'http://webfinger.net/rel/profile-page',
|
||||
type: 'text/html',
|
||||
href: `https://example.com/@${member.slug}`
|
||||
},
|
||||
{
|
||||
rel: 'http://webfinger.net/rel/avatar',
|
||||
type: 'image/jpeg',
|
||||
href: member.data.avatar
|
||||
}
|
||||
],
|
||||
propertiesBuilder: (member) => ({
|
||||
'http://schema.org/name': member.data.name
|
||||
})
|
||||
}]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### OpenID Connect
|
||||
|
||||
Provide issuer discovery for authentication:
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
webfinger: {
|
||||
enabled: true,
|
||||
resources: [
|
||||
{
|
||||
resource: 'https://example.com',
|
||||
links: [
|
||||
{
|
||||
rel: 'http://openid.net/specs/connect/1.0/issuer',
|
||||
href: 'https://example.com'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Team Profiles
|
||||
|
||||
Make team members discoverable:
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
webfinger: {
|
||||
enabled: true,
|
||||
collections: [{
|
||||
name: 'team',
|
||||
resourceTemplate: 'acct:{data.email}',
|
||||
aliasesBuilder: (member) => [
|
||||
`https://example.com/team/${member.slug}`,
|
||||
member.data.website
|
||||
].filter(Boolean),
|
||||
propertiesBuilder: (member) => ({
|
||||
'http://schema.org/name': member.data.name,
|
||||
'http://schema.org/jobTitle': member.data.role,
|
||||
'http://schema.org/email': member.data.email
|
||||
}),
|
||||
linksBuilder: (member) => [
|
||||
{
|
||||
rel: 'http://webfinger.net/rel/profile-page',
|
||||
type: 'text/html',
|
||||
href: `https://example.com/team/${member.slug}`
|
||||
},
|
||||
...(member.data.github ? [{
|
||||
rel: 'http://webfinger.net/rel/profile-page',
|
||||
type: 'text/html',
|
||||
href: `https://github.com/${member.data.github}`,
|
||||
titles: { en: 'GitHub Profile' }
|
||||
}] : [])
|
||||
]
|
||||
}]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Blog Authors
|
||||
|
||||
Link blog authors to their profiles:
|
||||
|
||||
```typescript
|
||||
discovery({
|
||||
webfinger: {
|
||||
enabled: true,
|
||||
collections: [{
|
||||
name: 'authors',
|
||||
resourceTemplate: 'acct:{slug}@example.com',
|
||||
linksBuilder: (author) => [
|
||||
{
|
||||
rel: 'http://webfinger.net/rel/profile-page',
|
||||
href: `https://example.com/authors/${author.slug}`
|
||||
},
|
||||
{
|
||||
rel: 'http://webfinger.net/rel/avatar',
|
||||
href: author.data.avatar,
|
||||
type: 'image/jpeg'
|
||||
}
|
||||
],
|
||||
propertiesBuilder: (author) => ({
|
||||
'http://schema.org/name': author.data.name,
|
||||
'http://schema.org/description': author.data.bio
|
||||
})
|
||||
}]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Query Format
|
||||
|
||||
WebFinger is queried via HTTP GET with query parameters:
|
||||
|
||||
```
|
||||
GET /.well-known/webfinger?resource=acct:alice@example.com
|
||||
GET /.well-known/webfinger?resource=acct:alice@example.com&rel=self
|
||||
```
|
||||
|
||||
**Required parameter:**
|
||||
- `resource`: The resource identifier (e.g., `acct:alice@example.com`)
|
||||
|
||||
**Optional parameter:**
|
||||
- `rel`: Filter links by relation type
|
||||
|
||||
## Response Format (JRD)
|
||||
|
||||
WebFinger returns JSON Resource Descriptor (JRD):
|
||||
|
||||
```json
|
||||
{
|
||||
"subject": "acct:alice@example.com",
|
||||
"aliases": [
|
||||
"https://example.com/@alice"
|
||||
],
|
||||
"properties": {
|
||||
"http://schema.org/name": "Alice Developer"
|
||||
},
|
||||
"links": [
|
||||
{
|
||||
"rel": "http://webfinger.net/rel/profile-page",
|
||||
"type": "text/html",
|
||||
"href": "https://example.com/@alice"
|
||||
},
|
||||
{
|
||||
"rel": "self",
|
||||
"type": "application/activity+json",
|
||||
"href": "https://example.com/users/alice"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Template Variables
|
||||
|
||||
### Available Variables
|
||||
|
||||
- `{slug}`: Collection entry slug
|
||||
- `{id}`: Collection entry ID
|
||||
- `{data.fieldName}`: Access entry data fields
|
||||
- `{data.nested.field}`: Access nested fields
|
||||
- `{siteURL}`: Site hostname
|
||||
|
||||
### Examples
|
||||
|
||||
```typescript
|
||||
// Basic slug
|
||||
resourceTemplate: 'acct:{slug}@example.com'
|
||||
// Result: acct:alice@example.com
|
||||
|
||||
// Data field
|
||||
resourceTemplate: 'acct:{data.email}'
|
||||
// Result: acct:alice@company.com
|
||||
|
||||
// Nested field
|
||||
resourceTemplate: 'acct:{data.social.mastodon}'
|
||||
// Result: acct:alice@mastodon.social
|
||||
|
||||
// Site URL
|
||||
resourceTemplate: 'acct:{slug}@{siteURL}'
|
||||
// Result: acct:alice@example.com
|
||||
```
|
||||
|
||||
## Common Link Relations
|
||||
|
||||
- `self`: The resource itself
|
||||
- `http://webfinger.net/rel/profile-page`: Profile page
|
||||
- `http://webfinger.net/rel/avatar`: Avatar image
|
||||
- `http://openid.net/specs/connect/1.0/issuer`: OpenID issuer
|
||||
- `http://webfinger.net/rel/me`: Personal URL
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use standard relations:** Stick to IANA-registered or well-known rel values
|
||||
2. **Include types:** Always specify `type` for links when applicable
|
||||
3. **Provide aliases:** Help users find resources via multiple identifiers
|
||||
4. **Use URI properties:** Property names must be URIs (e.g., `http://schema.org/name`)
|
||||
5. **Enable CORS:** WebFinger responses include `Access-Control-Allow-Origin: *`
|
||||
6. **Cache appropriately:** Default 1-hour cache is usually sufficient
|
||||
|
||||
## Technical Notes
|
||||
|
||||
- **Dynamic route:** Not prerendered, handles queries at request time
|
||||
- **CORS enabled:** Returns `Access-Control-Allow-Origin: *`
|
||||
- **Media type:** `application/jrd+json`
|
||||
- **404 handling:** Returns 404 for unknown resources
|
||||
- **Rel filtering:** Supports `?rel=` parameter for link filtering
|
||||
|
||||
## Output Location
|
||||
|
||||
- **Endpoint:** `/.well-known/webfinger`
|
||||
- **URL:** `https://example.com/.well-known/webfinger?resource=acct:user@example.com`
|
||||
- **Cache-Control:** `public, max-age=3600` (1 hour, configurable via [caching](/reference/cache/))
|
||||
- **RFC:** [RFC 7033](https://datatracker.ietf.org/doc/html/rfc7033)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user