Documentation
¶
Overview ¶
Package plugins defines Aether’s public plugin architecture.
Aether plugins allow developers to extend the Aether system without modifying its core. Plugins are optional, modular components that provide additional data sources, transformations, or renderers.
Plugins MUST respect Aether’s legal and ethical principles:
- Robots.txt compliance
- No CAPTCHA bypassing
- No authentication circumvention
- No scraping of disallowed or paywalled content
Aether exposes three primary plugin types:
SourcePlugin ---------------- A SourcePlugin provides new legal/public data sources. It receives a free-form query string and returns a plugins.Document representing the fetched content. Source plugins integrate with Aether’s SmartQuery routing engine and high-level Search pipeline.
Examples: • Custom Hacker News retrieval • Open government datasets • Legally accessible public JSON APIs • Local filesystem loaders
TransformPlugin ---------------- A TransformPlugin receives a normalized plugins.Document and returns a modified/enriched version. Transform plugins are ideal for:
• Metadata enrichment • Summaries • Entity extraction • Keyword extraction
Transform plugins are applied after the primary Source pipeline and before final display output.
DisplayPlugin ---------------- A DisplayPlugin renders a plugins.Document into an alternative output format. Markdown is built into Aether’s Display subsystem, but DisplayPlugins allow:
• ANSI-styled CLI output • HTML rendering • TOON visualization • PDF generation (via legal libraries)
Display plugins are connected to the Aether Client but operate outside the Markdown core.
Plugin Registration ¶
Plugins are registered through methods exposed on the *aether.Client, for example:
cli := aether.NewClient(...) cli.RegisterSourcePlugin(myPlugin) cli.RegisterTransformPlugin(enricher) cli.RegisterDisplayPlugin(htmlRenderer)
Aether maintains a thread-safe plugin registry. Source plugins are invoked based on SmartQuery routing rules or explicit use. Transform plugins apply to normalized outputs. Display plugins expose new output formats.
Plugin Safety Model ¶
Plugins must never:
- Modify Aether’s HTTP fetcher
- Perform raw HTTP calls that ignore robots.txt
- Attempt to bypass protections or access restricted content
Plugins MAY:
- Call public APIs that explicitly allow programmatic access
- Use Aether’s own high-level APIs (Search, OpenAPI, RSS)
- Operate entirely on normalized documents
Aether enforces these rules by controlling fetch operations: plugins do not receive direct access to the internal HTTP client.
Plugin Document Model ¶
Plugins work with the plugins.Document structure. This structure is intentionally similar to internal/model.Document, but independent of internal implementation details. Aether converts between these two formats during plugin execution.
The plugin Document format is intentionally LLM-friendly and structured.
Purpose and Philosophy ¶
Aether plugins are designed to be:
- Simple to implement
- Safe by design
- Flexible enough for enterprise extension
- Stable across Aether versions
This package provides only interface definitions and documentation. Registration logic, routing, and execution glue live in the aether package and internal/plugin_registry components.
Index ¶
- type DisplayPlugin
- type Document
- type DocumentKind
- type Registry
- func (r *Registry) FindDisplayByFormat(format string) DisplayPlugin
- func (r *Registry) GetDisplay(name string) DisplayPlugin
- func (r *Registry) GetSource(name string) SourcePlugin
- func (r *Registry) GetTransform(name string) TransformPlugin
- func (r *Registry) ListDisplayFormats() []string
- func (r *Registry) ListDisplays() []string
- func (r *Registry) ListSources() []string
- func (r *Registry) ListTransforms() []string
- func (r *Registry) RegisterDisplay(p DisplayPlugin) error
- func (r *Registry) RegisterSource(p SourcePlugin) error
- func (r *Registry) RegisterTransform(p TransformPlugin) error
- type Section
- type SectionRole
- type SourcePlugin
- type TransformPlugin
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type DisplayPlugin ¶
type DisplayPlugin interface {
// Name returns a stable identifier for the display plugin.
Name() string
// Description explains what output this plugin produces.
Description() string
// Format returns a short format tag such as:
// "ansi", "html", "pdf", "text", "custom"
Format() string
// Render converts the given Document into the plugin's output
// format. The returned byte slice may contain text, HTML, binary
// document data, etc., depending on Format().
Render(ctx context.Context, doc *Document) ([]byte, error)
}
DisplayPlugin renders a Document into a non-Markdown format.
Examples:
- ANSI-colored CLI view
- HTML view
- PDF output (via a legal PDF library)
- TOON-derived visual representations
Stage 13 defines the interface, but Aether will connect this into the Display subsystem and CLI/TUI layers in later stages.
type Document ¶
type Document struct {
// Source is a human-readable identifier for the origin of this
// document, e.g. "plugin:hackernews", "plugin:my_api", etc.
Source string `json:"source,omitempty"`
// URL is an optional canonical URL for the document, if any.
URL string `json:"url,omitempty"`
// Kind is the broad category of the document (article, feed, text, etc.).
Kind DocumentKind `json:"kind,omitempty"`
// Title is the main title or name of the document.
Title string `json:"title,omitempty"`
// Excerpt is a short summary or teaser.
Excerpt string `json:"excerpt,omitempty"`
// Content is the primary body text, if there is a single dominant
// body for the document.
Content string `json:"content,omitempty"`
// Metadata is an arbitrary, flat key/value map for additional data.
Metadata map[string]string `json:"metadata,omitempty"`
// Sections is an optional list of structured sections, such as
// article body blocks, feed entries, or metadata sections.
Sections []Section `json:"sections,omitempty"`
}
Document is the main data structure plugins work with.
It is intentionally similar (but not identical) to Aether's internal normalized document model. Aether will convert between this type and its internal representation during registration / execution.
Plugin authors should focus on filling in as much useful, LLM-friendly content as possible (Title, Excerpt, Content, Sections, Metadata).
type DocumentKind ¶
type DocumentKind string
DocumentKind represents the broad category of a plugin-produced document.
This is intentionally similar to Aether's internal normalized document kinds, but defined separately here to avoid import cycles and to keep the plugin API stable even if internals change.
const ( DocumentKindUnknown DocumentKind = "unknown" DocumentKindArticle DocumentKind = "article" DocumentKindHTML DocumentKind = "html_page" DocumentKindFeed DocumentKind = "feed" DocumentKindJSON DocumentKind = "json" DocumentKindText DocumentKind = "text" DocumentKindBinary DocumentKind = "binary" )
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry holds all registered plugins. It is safe for concurrent use by the Aether client and plugins.
Aether creates one Registry per client instance.
func NewRegistry ¶
func NewRegistry() *Registry
NewRegistry constructs an empty, thread-safe plugin registry. This function is used internally by the Aether client.
func (*Registry) FindDisplayByFormat ¶
func (r *Registry) FindDisplayByFormat(format string) DisplayPlugin
func (*Registry) GetDisplay ¶
func (r *Registry) GetDisplay(name string) DisplayPlugin
GetDisplay returns a DisplayPlugin by name.
func (*Registry) GetSource ¶
func (r *Registry) GetSource(name string) SourcePlugin
GetSource returns a SourcePlugin by name, or nil if not found.
func (*Registry) GetTransform ¶
func (r *Registry) GetTransform(name string) TransformPlugin
GetTransform returns a TransformPlugin by name.
func (*Registry) ListDisplayFormats ¶
ListDisplayFormats returns all supported display formats deduplicated and lexicographically sorted.
func (*Registry) ListDisplays ¶
ListDisplays returns a sorted list of display plugin names.
func (*Registry) ListSources ¶
ListSources returns a sorted list of SourcePlugin names.
func (*Registry) ListTransforms ¶
ListTransforms returns a sorted list of transform plugin names.
func (*Registry) RegisterDisplay ¶
func (r *Registry) RegisterDisplay(p DisplayPlugin) error
RegisterDisplay registers a DisplayPlugin. Names must be unique.
func (*Registry) RegisterSource ¶
func (r *Registry) RegisterSource(p SourcePlugin) error
RegisterSource registers a SourcePlugin. Names must be unique.
func (*Registry) RegisterTransform ¶
func (r *Registry) RegisterTransform(p TransformPlugin) error
RegisterTransform registers a TransformPlugin. Names must be unique.
type Section ¶
type Section struct {
Role SectionRole `json:"role,omitempty"`
Title string `json:"title,omitempty"`
Text string `json:"text,omitempty"`
Meta map[string]string `json:"meta,omitempty"`
}
Section is a logical chunk of content within a plugin Document, such as an article body, a feed item, or a metadata block.
type SectionRole ¶
type SectionRole string
SectionRole is a free-form role label for a document section. Common examples: "body", "summary", "feed_item", "metadata".
type SourcePlugin ¶
type SourcePlugin interface {
// Name returns a short, stable identifier for the plugin.
// Example: "hackernews", "my_org_api", "eu_press".
Name() string
// Description returns a human-readable explanation of what the
// plugin does, which may be surfaced in logs or introspection.
Description() string
// Capabilities declares what kinds of queries or intents this
// plugin is good at handling. Examples:
//
// []string{"news", "tech", "hn"}
// []string{"weather"}
//
// Aether’s SmartQuery router can use these tags to decide when
// to invoke the plugin.
Capabilities() []string
// Fetch executes a plugin-specific retrieval based on the incoming
// query. The query may be a free-form question, a keyword, or a
// structured string depending on how the plugin is used.
//
// The returned Document should be as normalized and clean as the
// plugin can reasonably make it.
Fetch(ctx context.Context, query string) (*Document, error)
}
SourcePlugin is responsible for providing new documents from legal, public data sources. Examples:
- A Hacker News source plugin
- A public government dataset plugin
- A custom internal API plugin (when used in a private deployment)
SourcePlugin implementations should be stateless or internally safe for concurrent use.
type TransformPlugin ¶
type TransformPlugin interface {
// Name returns a stable identifier for this transform plugin.
Name() string
// Description describes what transformation this plugin performs.
Description() string
// Apply transforms the input Document and returns the resulting
// Document. Implementations may modify the input in place or
// allocate a new Document, but must document their behavior.
Apply(ctx context.Context, doc *Document) (*Document, error)
}
TransformPlugin receives an existing Document and returns a new (or modified) Document. Examples:
- Post-hoc summarization
- Keyword extraction
- Entity extraction
- Additional metadata enrichment
Transform plugins must be pure functions in spirit: given the same input Document, they should produce the same output Document, barring non-deterministic external services.