Documentation
¶
Overview ¶
Package web: app-contract search endpoint (ADR-0028). GET /_search?q= returns the caller's user-scoped matches; platformd fans out to every app and groups the results.
Package web is the server scaffold every app runs on (docs/design/architecture.md). It owns listening, identity enforcement, /healthz, request logging, and graceful shutdown, so apps contain zero infrastructure code — just routes.
Index ¶
- func Changed(login string)
- func DashboardCard(mux *http.ServeMux, provider CardProvider)
- func EnableChat(mux *http.ServeMux, slug string, provider ChatProvider)
- func EnableChatWithTools(mux *http.ServeMux, slug string, provider ChatProvider, toolSource ToolSource, ...)
- func IdempotencyKey(ctx context.Context) (string, bool)
- func Intent(mux *http.ServeMux, appTitle string, def IntentDef)
- func Live(mux *http.ServeMux, ...)
- func Notify(login string)
- func Run(slug string, register func(mux *http.ServeMux))
- func Search(mux *http.ServeMux, provider SearchProvider)
- func Serve(slug string, defaultPort int, register func(mux *http.ServeMux))
- func Skills(mux *http.ServeMux, skillsFS fs.FS) error
- func Tool(mux *http.ServeMux, def ToolDef)
- type AutomationMode
- type AutomationPolicy
- type CardProvider
- type ChatProvider
- type IntentDef
- type SSE
- type SearchProvider
- type SearchResult
- type SkillDef
- type ToolDef
- type ToolSource
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Changed ¶
func Changed(login string)
Changed announces that the user's data in THIS app changed: local /_live subscribers re-render, and platformd is nudged (fire-and-forget) so dashboard cards refresh too. Call after every mutation — handlers, tools, and intents alike.
func DashboardCard ¶
func DashboardCard(mux *http.ServeMux, provider CardProvider)
DashboardCard mounts GET /_card, the optional app-contract endpoint the dashboard pulls per-user summaries from. Call inside web.Run's register:
web.DashboardCard(mux, func(ctx, user) (templ.Component, error) { … })
func EnableChat ¶
func EnableChat(mux *http.ServeMux, slug string, provider ChatProvider)
EnableChat mounts POST /_chat and turns on the AppShell chat panel. Call once inside web.Run's register function:
web.EnableChat(mux, "journal", func(ctx, user) (string, error) { … })
The chat is agentic over the app's own web.Tool registrations.
func EnableChatWithTools ¶
func EnableChatWithTools(mux *http.ServeMux, slug string, provider ChatProvider, toolSource ToolSource, extra ...llm.Option)
EnableChatWithTools is EnableChat with an explicit tool source. Extra llm options are applied to every chat completion — platformd's dashboard chat uses this for llm.WithBuiltins (ADR-0024); apps normally pass none.
func Intent ¶
Intent mounts GET /_intents/<name> (prefilled, editable confirm page) and POST /_intents/<name> (execute → redirect). Other apps link here via the registry; the confirm step doubles as the edit step.
func Live ¶
func Live(mux *http.ServeMux, fragment func(ctx context.Context, user auth.User) (templ.Component, error))
Live mounts GET /_live: a Datastar SSE stream that re-renders the app's live region on every Changed(login) and patches it in place. Pages subscribe from a stable wrapper element:
<div data-init="@get('/_live')">
<div id="<slug>-live">…initial render…</div>
</div>
fragment must render the element with that same id.
func Notify ¶
func Notify(login string)
Notify wakes this process's /_live subscribers for a user without touching the plane — platformd's /notify endpoint feeds cross-process changes in through here.
func Run ¶
Run serves the app named slug on the port from its manifest — the normal entrypoint for apps. Production units override the address with -listen <tailscale-ip>:<port> (docs/adr/0011-split-host-deployment.md).
func Search ¶ added in v0.2.0
func Search(mux *http.ServeMux, provider SearchProvider)
Search mounts GET /_search, the optional app-contract endpoint the dashboard search box and the platform `search` tool fan out to. Call inside web.Run's register.
func Serve ¶
Serve is Run with an explicit default port, for platform processes that have no app manifest (platformd). Every route registered by register is behind auth.Middleware; /healthz is not. Blocks until SIGINT/SIGTERM, then drains for up to 5s.
func Skills ¶
Skills registers the app's bundled skills (ADR-0026): skillsFS holds <name>/SKILL.md files with `name:` and `description:` frontmatter — the same format as the repo's agent skills. It mounts GET /_skills (the parsed set, JSON) and registers one `load_skill` tool whose description carries the index, so every surface with the app's tools — its own chat, the dashboard chat (as <slug>_skill), external MCP clients — can load a skill's full instructions on demand. Call inside web.Run's register, before EnableChat.
Types ¶
type AutomationMode ¶ added in v0.13.0
type AutomationMode string
const ( AutomationForbidden AutomationMode = "forbidden" AutomationReadOnly AutomationMode = "read_only" AutomationIdempotent AutomationMode = "idempotent" )
type AutomationPolicy ¶ added in v0.13.0
type AutomationPolicy struct{ Mode AutomationMode }
type CardProvider ¶
CardProvider renders the app's live dashboard-card fragment for a user (ADR-0017): content only — no AppShell, no forms; cheap queries, never LLM calls (the dashboard waits behind a short timeout).
type ChatProvider ¶
ChatProvider returns the current user's relevant app data as text — the context the in-app chat answers from (ADR-0015). Keep it recent and bounded (e.g. last 30 days), not a full dump.
type IntentDef ¶
type IntentDef struct {
Name string // must match an [[intents]] name in app.toml
Title string // confirm-page heading, e.g. "Create Todo"
Prompt string // label over the editable payload, e.g. "Task description"
Handler func(ctx context.Context, user auth.User, text string) (redirect string, err error)
}
IntentDef wires a manifest-declared intent (docs/adr/0018-cross-app-intents.md) to its handler. Handler returns the in-app URL to land on after execution (the created thing, or "/").
type SSE ¶
type SSE = datastar.ServerSentEventGenerator
SSE is the Datastar server-sent-events generator apps use for live UI (patch elements/signals from handlers). Kept as an alias so apps import only pkg/web; the AppShell already loads the matching datastar.js.
type SearchProvider ¶ added in v0.2.0
SearchProvider returns the user's results matching q. It MUST scope to the user and MUST be a cheap DB query — never an LLM call (platformd waits behind a short timeout).
type SearchResult ¶ added in v0.2.0
type SearchResult struct {
Title string `json:"title"`
Snippet string `json:"snippet,omitempty"`
URL string `json:"url,omitempty"`
Timestamp string `json:"timestamp,omitempty"`
}
SearchResult is one hit an app returns for a query. Only Title is required; URL is app-relative and best-effort deep (a bare "/" home link is acceptable). See docs/specs/app-search.md.
type SkillDef ¶
type SkillDef struct {
Name string `json:"name"`
Description string `json:"description"`
Body string `json:"body"`
}
SkillDef is one bundled skill: procedural knowledge for chat, loaded on demand (ADR-0026).
type ToolDef ¶
type ToolDef struct {
Name string
Description string
Schema map[string]any // JSON Schema for the arguments object
Automation AutomationPolicy
Handler func(ctx context.Context, user auth.User, args json.RawMessage) (string, error)
}
ToolDef is a user-scoped action the app exposes to LLMs (ADR-0021): agentic chat calls these through the gateway, and the platform MCP server exposes them to external clients as <slug>_<name>. Handlers must scope everything by the given user — same discipline as HTTP handlers.