Documentation
¶
Overview ¶
The design-system gallery is generated and is not committed: it is 20 MB of machine output, rewritten whole every time a ui partial changes, and the website that publishes it builds it instead by running cmd/dsgen against a pinned version of this module.
`go generate ./...` writes a copy into .design-system/, which is git-ignored. It is there to be looked at — open a page, diff two runs, see what a change to a partial did to every page that renders it — and nothing reads it back. Deleting it costs nothing.
Package rastrillo is the CARLOS web framework — the shape of a CARLOS app, the way carlosframework/platform is the shape of the deployment substrate it runs on. See the design doc for the full picture: https://github.com/carlosframework/platform/blob/main/docs/superpowers/specs/2026-08-01-carlos-framework-design.md
The root package holds the process shape (Run/Serve/Handler, the activation contract, the SQLite opener, migrations), the action vocabulary (Ctx, Actor), the manifest vocabulary (Resource, Tool), fingerprinted assets, and localization. The subsystems live beside it: crypto (the family envelope), auth (keymail sign-in with the magic-link fallback), webauthn, eventlog (the Mergeable store), blobs, mail, carlos (the platform's scheduled-work contract), tools (agent dispatch), and ui (the component partials). README.md keeps the honest status list.
Index ¶
- Constants
- Variables
- func BaseCatalogs() map[string]Catalog
- func BaseKeys() []string
- func BaseLocales() []string
- func Dir(locale string) string
- func Handler(opts Options) (http.Handler, func() error, error)
- func Icon(slug string) template.HTML
- func IconSlugs() []string
- func IsBaseKey(key string) bool
- func LocaleFrom(r *http.Request) string
- func NewRef() string
- func OpenDB(path string, migrations []string) (*sql.DB, error)
- func Run(opts Options) error
- func Serve(opts Options) error
- func T(r *http.Request, key string) string
- func Tf(r *http.Request, key string, args ...any) string
- func WithActor(r *http.Request, a Actor) *http.Request
- type Access
- type Actor
- type Assets
- type Catalog
- type Column
- type Ctx
- type ErrorPageFunc
- type Field
- type Filter
- type Form
- type Kind
- type List
- type LocaleItem
- type Locales
- func (l *Locales) Codes() []string
- func (l *Locales) Default() string
- func (l *Locales) FrameworkHas(code string) bool
- func (l *Locales) Has(code string) bool
- func (l *Locales) Middleware(next http.Handler) http.Handler
- func (l *Locales) SwitchHandler() http.Handler
- func (l *Locales) T(locale, key string) string
- func (l *Locales) Tf(locale, key string, args ...any) string
- type Options
- type RenderFunc
- type Resource
- type ScopeKind
- type StoreKind
- type Tool
- type ToolDef
Constants ¶
const LocaleCookie = "rastrillo_locale"
LocaleCookie is the stored-preference cookie the resolution chain consults right after the path prefix, before Accept-Language. Design doc §10 names "a stored preference" without naming the mechanism; a cookie is the only one that survives §9's zero-JS baseline.
const LocaleSwitchPath = "/_locale"
LocaleSwitchPath is the framework route the language switcher POSTs to (spec §2.4). Mounted by Serve whenever Options.Locales is set.
Variables ¶
var BuildVersion = "dev"
BuildVersion is what GET /api/version reports. The scaffolded Makefile's release target stamps it with -X from git describe, and refuses to build rather than stamping an empty string or a dirty tree — see makefileTemplate in cmd/rastrillo/new.go.
The platform's deploy verification polls GET /api/version on every instance socket — see blueprint.md, "The carlos core": "every instance must also serve GET /api/version reporting its build sha."
Why the stamp is the point rather than a nicety: carlos deploy checks the router's x-carlos-version header, which is the release the platform believes it ADOPTED. /api/version is what the process RUNNING on the instance says it is. They are two different facts, and the deploy is only verified when they agree. A binary that answers "dev" from every build ever made cannot disagree with anything, so a process that was never recycled onto the new release verifies green — which happened, to a real app, with deploy printing "live". Every app scaffolded before v0.23.0 has that hole in it; a re-scaffolded Makefile, or the two lines from it, closes it.
"dev" stays the default on purpose. rastrillo dev and a plain go build do not stamp, and a binary that says "dev" is saying something true about itself.
Functions ¶
func BaseCatalogs ¶
BaseCatalogs returns a copy of every shipped catalog, keyed by locale code exactly as declared in BaseLocales.
func BaseKeys ¶
func BaseKeys() []string
BaseKeys returns the sorted rastrillo.ui.* key set — what an app declaring a locale the framework does not ship has to translate before `rastrillo generate --check` passes (spec §3.4).
func BaseLocales ¶
func BaseLocales() []string
BaseLocales returns the shipped locale codes, en first.
func Dir ¶
Dir is the HTML dir attribute for a locale: "rtl" for the right-to-left scripts a rastrillo app can declare, "ltr" otherwise. Decided on the primary subtag, so ar-EG mirrors as ar does.
func Handler ¶
Handler is everything Serve builds short of the listener and the signal handling: it opens the database (if configured), applies migrations, resolves the Mux/Router choice, and assembles the full serving handler — framework endpoints, Wrap, locales and all. The returned close func releases the database handle (a no-op without one).
Exported for test harnesses: before this seam, every app's harness hand-duplicated /healthz, /api/version and the DSN pragma ordering because Serve blocks on a real listener (vitogo's vitotest says so in its own comments; seapointish copied the same shape). Now a harness is httptest.NewServer around this.
func Icon ¶
Icon renders one vendored icon by its Lucide slug for use as an html/template FuncMap entry:
tmpl.Funcs(template.FuncMap{"icon": rastrillo.Icon})
// then, in the template: {{icon "check"}}
An unknown slug renders nothing rather than panicking a page mid-response -- a typo must cost a missing icon, not a crash.
func IconSlugs ¶
func IconSlugs() []string
IconSlugs lists every slug Icon answers, sorted.
These names are rastrillo's own vocabulary, not any vendor's: five of the twelve differ from the names lucide.dev publishes. "kebab" is Lucide's ellipsis-vertical, and v1 renamed the other four (check-circle, alert-triangle, x-circle, help-circle). "menu" is NOT one of them — that is Lucide's own slug, and it is Font Awesome that calls the glyph "bars". internal/iconsets and docs/site/icons.md say the same five, and internal/iconsets.LucideName is where the mapping actually lives, read off the vendored glyph data so it cannot drift from it. An app scaffolded with a different set answers exactly this list too, which is what lets {{icon "search"}} mean the same thing everywhere and keeps the shipped ui/ partials set-agnostic.
Exported so tooling can check the two stay in step — internal/iconsets asserts every scaffoldable set covers all of it.
func IsBaseKey ¶
IsBaseKey reports whether key is one the framework ships.
func LocaleFrom ¶
LocaleFrom returns the locale Middleware resolved for r, or "" if the request never went through it.
func NewRef ¶
func NewRef() string
NewRef mints the short reference an error page shows and the log line carries: six lowercase base32 characters over 4 bytes of crypto/rand — 30 bits, enough that two errors in the same log window will not share one, and short enough that a person will actually quote it.
It is not a secret and not an id: nothing is stored under it. Its only job is to join what the user saw to what the operator grepped, which is why it appears in exactly two places — the page and the log.
func OpenDB ¶
OpenDB applies the SQLite convention the survey found hand-propagated, with fixes, repo to repo (design doc §5): busy_timeout set *before* journal_mode=WAL — the reverse order crashes with SQLITE_BUSY under concurrent open, titogo's real fix — then SetMaxOpenConns(1), then an eager ping so the file exists on disk from boot, then migrate.
Exported so tests and non-Serve contexts get the corrected opener instead of reproducing the DSN by hand (the blog's F4).
func Run ¶
Run is the process entrypoint for a rastrillo app: it resolves the platform's activation argv, then serves. The platform invokes an app binary in two shapes (see carlosframework/platform, internal/activator/backend_exec.go and internal/host/units/):
<binary> [-socket p] [-addr a] [-db p] agent exec child — hibernate
routes; the activator spawns
`<live> --socket <s> --db <d>`
<binary> serve carlos-app@.service unit
tenant — no flags; the listener
arrives via LISTEN_FDS (fd 3)
and state lives in
$STATE_DIRECTORY
<binary> sidecar run the host's sidecar — spawned
beside the instance when its
sidecar env file exists; runs
Options.Sidecar in a loop, no
listener (design doc §8)
Flags override the corresponding Options fields. A relative Options.DBPath (or -db value) is resolved inside $STATE_DIRECTORY when systemd provides one — a unit tenant's cwd is not its state dir — so the same binary and the same Options work in a dev checkout, as an exec child, and as a unit tenant. Hibernation needs nothing further from the app: the activator owns the restore/replicate cycle, and Serve's SIGTERM drain (10s) fits inside the activator's 20s budget.
func Serve ¶
Serve opens the database (if configured), applies migrations, resolves the platform's activation contract for a listener, and serves until the process receives SIGTERM/SIGINT. It always answers GET /healthz itself — the manifest/action layer never has to remember to.
Timeouts ¶
The server bounds two things for every app: how long a client may take to send its request headers (Options.ReadHeaderTimeout) and how long an idle keep-alive connection is kept open (Options.IdleTimeout). Neither can interrupt an in-flight request, which is what makes them safe as defaults.
Nothing here bounds a peer that stalls PART-WAY through a request body or a response. That is deliberate: net/http's ReadTimeout and WriteTimeout are total deadlines, so using them for that would cut off slow-but-healthy clients — a large upload, a git pack, an SSE feed, a WebSocket — along with the stalled ones. They are available on Options for apps that genuinely have only short requests, and off otherwise.
An app that streams must therefore bound its own streaming span, with an idle deadline re-armed as bytes move:
rc := http.NewResponseController(w) rc.SetWriteDeadline(time.Now().Add(idle)) // before each write
Set it on the side that can actually block. A handler copying from a subprocess pipe blocks on the READ of that pipe, not on the write to the client, and a write deadline never fires there — the pipe needs its own deadline (os.File supports one) and the child needs a process-group kill to reap grandchildren still holding it open. This is not hypothetical: it wedged a production app for 158 minutes on 2026-08-19.
func T ¶
T translates key in the request's resolved locale — the lookup an action calls. Outside a request that went through Middleware it returns the key verbatim rather than guessing a locale.
func Tf ¶
Tf is T plus {name} placeholder interpolation. See (*Locales).Tf for the accepted argument forms.
func WithActor ¶
WithActor stamps who is making this request onto its context — the tools dispatcher uses it so an agent call is attributed end to end. The generated router copies it onto Ctx.Actor after the app's ctxFactory runs, so an app factory that doesn't set Actor still gets honest attribution.
Types ¶
type Access ¶
type Access int
Access is what a tool may do — the registry's read/write split that drives §8's consent gating.
type Actor ¶
Actor identifies who is calling an action: a human request or a named agent. See the design doc §8 — every action's caller is attributed, never anonymous, so audit trails can say who did what honestly.
func ActorFromContext ¶
ActorFromContext reports the actor WithActor stamped, if any.
type Assets ¶
type Assets struct {
// contains filtered or unexported fields
}
Assets fingerprints an app's static files so they can be cached forever and still update on an ordinary reload (see the assets + TDD-scaffold design doc). Path maps a file to a URL carrying its content hash; Handler serves that URL with an immutable Cache-Control. Because the hash changes whenever the content does, the HTML always links a URL the browser has never cached stale.
The FS is served with http.FileServerFS semantics — URL path = "/" + FS path — matching how the scaffold embeds static/ (assets.go's //go:embed static): NewAssets(app.StaticFS), mounted at "GET /static/" with no StripPrefix.
func NewAssets ¶
NewAssets wraps a file tree — the scaffold's embedded StaticFS, or os.DirFS for an app serving a live directory — in a content-hash registry.
func (*Assets) Handler ¶
Handler serves the tree with the fingerprinting contract:
- a hashed name matching the file's current content is immutable — Cache-Control: public, max-age=31536000, immutable — because that exact URL can never serve different bytes;
- a hashed name that no longer matches (a stale page asking for an old version) serves the *current* content with no-cache: a slightly-stale stylesheet on a stale page beats a 404;
- a bare name serves no-cache, so deep links keep working;
- a real file whose name merely looks hashed wins over hash-stripping.
Mount it where the FS layout says — for the scaffold's embedded static/:
mux.Handle("GET /static/", assets.Handler())
func (*Assets) Path ¶
Path maps an FS path to its currently-hashed absolute URL path:
Path("static/tokens.css") → "/static/tokens.d1e8a70b5ccab1dc.css"
A missing file returns "/" + name unchanged, so the 404 surfaces at request time — visible in the network tab — instead of a render-time panic.
type Catalog ¶
Catalog is one locale's flat key → string table (design doc §10).
type Column ¶
type Column struct {
Field string `json:"field" toml:"field"`
Kind Kind `json:"kind" toml:"kind"` // zero value means Text
}
Column describes a column in a resource list.
type Ctx ¶
type Ctx struct {
DB *sql.DB
Logger *slog.Logger
// Assets is the app's fingerprinted static-file registry, when
// the app wires one — the scaffold does, over its embedded
// static/ tree. Actions link assets by hashed URL:
// ctx.Assets.Path("static/tokens.css"). Nil for an app that
// serves assets some other way — the same contract as DB.
Assets *Assets
// Actor records who is calling this action (design doc §8).
Actor Actor
// Render is the manifest system's seam (design doc's manifest
// slice): generated actions cannot call an app-private helper like
// a hand-rolled blog.Render, so they call ctx.Render instead. The
// app's ctx factory sets it (e.g. &rastrillo.Ctx{DB: db, Render:
// blog.Render}); a generated action nil-checks it and answers a
// logged 500 rather than a nil-pointer panic when an app forgets
// to wire it. See RenderFunc and internal/generate's action
// emitter for the exact page names a generated action calls it
// with.
Render RenderFunc
// ErrorPage renders the app's own error page — the seam view.Fail,
// view.NotFound and view.Forbidden call so that a failure inside a
// generated action looks like the rest of the app instead of
// net/http's bare text. Wire the same function to
// Options.ErrorPage and a panic gets the identical page:
//
// page := func(w http.ResponseWriter, r *http.Request, status int, ref string) {
// blog.RenderError(w, r, status, ref) // ui's "error-page" partial
// }
// // in the ctx factory: &rastrillo.Ctx{DB: db, ErrorPage: page}
// // in Options: ErrorPage: page
//
// Nil is legal and is the default: the helpers answer plain text,
// which is honest, ugly, and exactly what an app that has not
// thought about its error pages should see.
ErrorPage ErrorPageFunc
}
Ctx is passed to every action: the app's own wiring — its database, logger, asset registry, and the Render seam generated actions call through — built once by the app's ctxFactory. Per-request state doesn't live here: identity lives in sessions.Current(r) / sessions.UserID(r), and locale is rastrillo.LocaleFrom(r), both read straight off the request rather than staged onto Ctx.
type ErrorPageFunc ¶
ErrorPageFunc renders an error response body: the app's own page, in its own shell, for a status the framework or a generated action reached rather than the app. ref is the NewRef the failure was logged under, empty for the statuses that have nothing to reference (404, 403). It is the type of both Ctx.ErrorPage and Options.ErrorPage — one shape, so an app writes the function once and wires it to both.
The callback owns the status code as well as the body: it must call WriteHeader(status) itself.
type Field ¶
type Field struct {
Name string `json:"name" toml:"name"`
Kind Kind `json:"kind" toml:"kind"` // zero value means Text
Required bool `json:"required" toml:"required"`
}
Field describes an input field in a form.
type Filter ¶
type Filter struct {
Field string `json:"field" toml:"field"`
Values []string `json:"values" toml:"values"`
}
Filter specifies a column and a set of values for filtering a list.
type Form ¶
type Form struct {
Basics []Field `json:"basics" toml:"basics"`
Advanced []Field `json:"advanced" toml:"advanced"`
}
Form describes the form views for creating and editing a resource.
type List ¶
type List struct {
Columns []Column `json:"columns" toml:"columns"`
Search bool `json:"search" toml:"search"`
Filter []string `json:"filter" toml:"filter"` // superseded by Filters; still validated, generates the WHERE clause but no control.
Filters []Filter `json:"filters" toml:"filters"`
}
List describes the table view for a resource.
type LocaleItem ¶
LocaleItem is one entry of the language switcher: the declared code, its autonym (rastrillo.ui.locale_name in that locale, or the code when no catalog names it), a plain link to the same path under that locale's prefix, and whether it is the request's locale.
func LocaleItems ¶
func LocaleItems(r *http.Request) []LocaleItem
LocaleItems builds the switcher's data for r. Empty when the request never went through Middleware or the app declares one locale — the partial renders nothing for an empty list, so a one-locale app can call it unconditionally.
type Locales ¶
type Locales struct {
// contains filtered or unexported fields
}
Locales is an app's declared locale set, its own catalogs, and the framework's base English catalog underneath them.
Lookup is layered, in this order: the requested locale's app catalog, the default locale's app catalog, the framework's catalog for the requested locale, when it ships one, the base catalog, then the key itself. The middle layer is design doc §10's "missing keys fall back to the declared default locale during development"; the base layer is what lets a single-locale app get correctly-worded built-in components without writing a catalog at all. Returning the key — never "" — keeps a missing string visible on the page instead of silently blanking a sentence.
func NewLocales ¶
NewLocales validates the declared set and reads locales/<code>.toml out of fsys for each declared code. fsys may be nil (framework base catalog only). A declared locale with no catalog file is not an error: a single-locale app declares "en" and ships no locales/ directory.
func (*Locales) Codes ¶
Codes returns the declared locale codes in declaration order.
func (*Locales) Default ¶
Default returns the declared default locale.
func (*Locales) FrameworkHas ¶
FrameworkHas reports whether the framework ships a base catalog for a declared code — matched exactly, so "zh" never finds "zh-Hans" (spec §3.3).
func (*Locales) Has ¶
Has reports whether code is one of the declared locales.
func (*Locales) Middleware ¶
Middleware resolves this request's locale and puts it, and this set, on the request context for LocaleFrom/T/Tf.
Precedence: URL path prefix, then the stored-preference cookie, then Accept-Language, then the default. The original design doc (§10) put the cookie last; that was reversed on 2026-08-28 when the framework started writing the cookie itself (SwitchHandler) — a stored choice that Accept-Language could override on the next request would make the switcher decorative.
A matched locale prefix is stripped from the path before the app's mux sees it, so one route serves every locale. §10's zero-JS locale switch is "a plain link to the same path under a different locale prefix", which only works if /fr/orders and /orders reach the same handler.
func (*Locales) SwitchHandler ¶
SwitchHandler answers POST /_locale: it stores the chosen locale in LocaleCookie and 303s to the return path under that locale's prefix. Same-origin is checked the way every mutating route in this framework checks it (csrf.SameOrigin), with the origin taken from the request itself — the handler has no configured origin and needs none, because the check is "did a page of ours submit this".
func (*Locales) T ¶
T looks key up for locale, layered per this type's doc comment.
func (*Locales) Tf ¶
Tf is T plus {name} placeholder interpolation — design doc §10's `{{Tf "key" .Args}}`. args are slog-style alternating name/value pairs (the convention this repo already uses for key/value lists), or a single map[string]any / map[string]string, which is exactly what the doc's own .Args example passes.
type Options ¶
type Options struct {
// Mux is the app's router — normally gen/router.go's output (design
// doc §4). Exactly one of Mux and Router must be set.
Mux *http.ServeMux
// Router, if set, builds the app's mux after the database opens:
// Serve calls it with the *sql.DB opened from DBPath — pragmas,
// eager ping, and Migrations already applied — and serves the mux
// it returns. This is how an app puts the framework-opened handle
// in its per-request Ctx without hand-copying the DSN (the blog's
// friction log, F4):
//
// Router: func(db *sql.DB) (*http.ServeMux, error) {
// return gen.Router(func(*http.Request) *rastrillo.Ctx {
// return &rastrillo.Ctx{DB: db, Logger: logger}
// }), nil
// },
//
// Exactly one of Mux and Router must be set. With DBPath empty,
// Router is called with a nil db — an app without a database can
// still defer its mux construction. Serve owns the handle and
// closes it when Serve returns; do not retain it past that. An app
// that needs a handle outside Serve's lifetime calls OpenDB itself.
Router func(db *sql.DB) (*http.ServeMux, error)
// Wrap, if set, wraps the app's mux — the one seam for app
// middleware: sessions, CSRF, panic pages, authorization
// (gleester's friction, James 2026-08-04; also the friction behind
// amadan's outer-catch-all-mux workaround). It runs inside the
// framework's chrome: GET /healthz and GET /api/version are
// answered outside it (platform probes never traverse app
// middleware), and locale-prefix stripping happens before it,
// so middleware sees the same paths routes match on. Nil means
// no wrapping. Returning nil is a boot error.
Wrap func(http.Handler) http.Handler
// DBPath, if set, opens a SQLite database with the pragma ordering
// and connection settings the survey found hand-propagated,
// error-prone, repo to repo (design doc §5): busy_timeout set
// *before* journal_mode=WAL, then SetMaxOpenConns(1).
DBPath string
// Migrations are applied in order at boot, idempotently: each must
// be safe to run against a database that already has it applied
// (CREATE TABLE IF NOT EXISTS, or an ALTER whose "duplicate column"
// error is ignored) — additive-only, per the family's hard-won rule.
Migrations []string
// Socket and Addr mirror the platform's activation contract (see
// testdata/echoapp in carlosframework/platform): a unix socket path,
// or a TCP host:port for local dev. If both are empty, Serve checks
// for a systemd-activated listener (LISTEN_FDS) before falling back
// to Addr ":8080".
Socket string
Addr string
// NextDue, if set, answers the platform's scheduled-wake poll: the
// activator asks a running instance GET /api/next-due (bearer
// $CARLOS_ADMIN_TOKEN) and hibernates knowing when to wake it —
// carlosframework/platform internal/activator/backend_exec.go. The
// returned time is the next moment the app has work; zero means
// nothing scheduled. Unset, the route does not exist and the
// activator treats the app as having no schedule (unit tenants
// never get the poll at all).
NextDue func() time.Time
// Sidecar is the app's sidecar pass — the wake → read since
// bookmark → decide → act loop's body (design doc §8). When the
// platform spawns `<binary> sidecar run` (it does exactly that when
// the host's sidecar env file exists), Run calls Sidecar in a loop:
// each pass returns when it has caught up, reporting when it next
// has scheduled work (zero: nothing scheduled — Run re-runs after
// a default poll interval). A pass error is logged and retried with
// backoff, never fatal: a sidecar outliving a flaky dependency is
// the point of having one. SIGTERM/SIGINT cancels the context and
// ends the loop. Nil with a `sidecar run` invocation is a loud
// startup error, not a silent serve.
Sidecar func(ctx context.Context) (time.Time, error)
// CSP replaces the value of the Content-Security-Policy header the
// framework sets on every response (see the package's default
// below: same-origin everything, no inline styles but pow's
// honeypot by hash, framing denied). Empty keeps the default. The
// other baseline headers — nosniff, frame denial, referrer policy, a
// one-year host-only HSTS — have no Options
// field on purpose: all of them — this one included — are set
// before any app code runs, so an app that wants different values
// sets (or deletes) its own in a handler or Options.Wrap middleware
// and simply wins.
CSP string
// Locales declares the app's locale codes (design doc §10) — the
// catalogs LocaleFS carries as locales/<code>.toml. Empty means a
// monolingual app: no locale middleware is installed and requests
// pay nothing.
Locales []string
// DefaultLocale is the locale for unprefixed requests that match
// nothing else, and the first fallback layer for missing keys.
// Empty defaults to Locales[0].
DefaultLocale string
// LocaleFS provides the locales/<code>.toml catalog files —
// normally an embed.FS rooted at the app directory. Nil is legal:
// lookups fall back to the key itself, which keeps a missing
// catalog visible instead of silently blank (§10).
LocaleFS fs.FS
// BaseCatalog optionally supplies a base catalog that sits UNDER
// every app catalog (Locales' own doc comment: requested locale's
// app catalog, then the default locale's app catalog, then this) —
// normally the generated gen/locales/locales.go var BaseCatalog a
// manifest resource's field labels and shared ui.* chrome strings
// compile to (design doc §9's manifest system; internal/generate's
// EmitLocales emits it from the same map as the human-readable
// gen/locales/en.toml, so the two cannot drift). Nil is legal — an
// app with no manifest resources has nothing to layer.
BaseCatalog Catalog
// Logger defaults to slog.Default() if nil.
Logger *slog.Logger
// ErrorPage renders the app's own error page for a failure the app
// never saw: today, a panic that reached the framework's recovery
// wrapper. Nil answers a plain "Something went wrong." — correct,
// and ugly enough that most apps will want to set it.
//
// It is the same function an app puts on Ctx.ErrorPage, which is
// what view.Fail/NotFound/Forbidden call, so a 500 from a handler
// and a 500 from a panic look identical to the person reading it.
// ui's error-page partial is the body; ref is the reference the
// failure was logged under.
//
// It is called only when nothing has been written yet in the
// common case; see recoverPanics for the mid-stream caveat.
ErrorPage ErrorPageFunc
// ReadHeaderTimeout bounds how long a client may take to send its
// request headers. Zero uses defaultReadHeaderTimeout. This is the
// slowloris bound: it costs a legitimate client nothing, because
// headers are small and sent up front.
ReadHeaderTimeout time.Duration
// IdleTimeout bounds how long an idle keep-alive connection is kept
// open between requests. Zero uses defaultIdleTimeout. It can never
// interrupt an in-flight request — only a connection doing nothing.
IdleTimeout time.Duration
// ReadTimeout and WriteTimeout are OFF by default (zero), and an app
// should think before setting them, because net/http applies them as
// TOTAL deadlines measured from the start of the request — not idle
// deadlines. A 40 MB upload over a slow link, a git pack streaming
// for minutes, a Server-Sent Events feed and a WebSocket are all
// legitimate and all unbounded in duration, and any of them is cut
// mid-flight by a total deadline no matter how healthy the peer is.
//
// Set these only for an app whose every request is known to be short
// (a JSON API with small bodies, say). To bound a STALLED peer on a
// long-lived request without capping a slow-but-healthy one, the tool
// is a per-handler idle deadline via http.ResponseController's
// SetReadDeadline/SetWriteDeadline, re-armed as bytes move — not
// these fields. See the package docs on Serve.
ReadTimeout time.Duration
WriteTimeout time.Duration
}
Options configures Serve.
func Resolve ¶
Resolve applies the platform's activation argv and environment to opts — everything Run does short of serving — and returns the result. Most apps just call Run; Resolve is the seam for the ones that need the resolved invocation first. The original motivating case — an app that opens its own database before building its mux — is better served by Options.Router now, which hands back the *sql.DB Serve opened; Resolve remains for apps that need the resolved paths themselves. If you do open your own handle and blank DBPath, the boot-materialization duty transfers with it: touch the driver (a Ping, or a migration) before Serve, or a hibernate route's activator replicates a file that does not exist.
type RenderFunc ¶
RenderFunc is how a generated action hands a page to the app's own template tree — the seam generated code needs because it cannot call an app-private helper (a hand-rolled blog.Render, say). Ctx.Render carries it; the app's ctx factory sets it, and a generated action nil-checks it before use. page is always one of "<resource>/list", "<resource>/show" or "<resource>/form" — internal/generate's action emitter documents and pins the exact contract (see actions.go).
type Resource ¶
type Resource struct {
Name string `json:"name" toml:"name"`
Route string `json:"route" toml:"route"`
Store StoreKind `json:"store" toml:"store"`
Scope ScopeKind `json:"scope,omitempty" toml:"scope"` // omitempty: an unscoped resource's manifest.json stays byte-identical to pre-Scope artifacts
List List `json:"list" toml:"list"`
Form Form `json:"form" toml:"form"`
}
Resource is one manifest: the §9 sugar a route opts into. Its JSON encoding (the struct tags here and on the types it embeds) is the generator's stable artifact — gen/manifest.json — consumed by any renderer; evolution is additive only. It describes a CRUD interface for a data entity.
type ScopeKind ¶
type ScopeKind string
ScopeKind categorizes who a resource's rows belong to.
const ( // Unscoped rows belong to the app: one shared table, no owner // column — the right shape for admin-style resources. Unscoped ScopeKind = "" // UserScoped rows belong to whoever created them: the generated // store adds an `owner` column holding the session Subject // (sessions.Session.Subject — a TEXT value, so keymail's email // subjects and password's numeric-string subjects both fit), every // generated query filters by it, and a row that isn't yours // answers 404 — the scope package's discipline, declared instead // of hand-written. Generated actions read the subject via // sessions.Current, so scoped routes must mount behind // sessions.Require / sessions.Middleware / auth.RequireSession. UserScoped ScopeKind = "user" )
type StoreKind ¶
type StoreKind string
StoreKind categorizes how a resource's data is stored and synchronized.
type Tool ¶
type Tool struct {
Description string
Access Access
// Args maps argument names to human/model-readable descriptions.
// At dispatch, an argument matching a {param} in the route fills
// that path segment; the rest travel as form values (POST and
// friends) or query parameters (GET/HEAD).
Args map[string]string
// Confirm is the consent sentence shown before a write executes —
// "{arg}" placeholders interpolate the call's arguments.
Confirm string
}
Tool marks an action as agent-callable. Declared in the action file itself, next to Handle:
var Tool = rastrillo.Tool{
Description: "Cancel one order and release its tickets.",
Access: rastrillo.ToolWrite,
Args: map[string]string{"id": "the order id"},
Confirm: "Cancel order {id}? Its tickets go back on sale.",
}
The generator reads it statically (the same AST pass that rewrites package clauses) and emits the registry into gen/tools.go. A ToolWrite with an empty Confirm fails `generate --check` — the buildable half of §13's agent-gate check.
Source Files
¶
- assets.go
- basecatalog.go
- ctx.go
- gen.go
- icons.go
- locale.go
- localemw.go
- manifest.go
- ref.go
- run.go
- serve.go
- sidecar.go
- tool.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package assertion signs short-lived identity handoffs between exact HTTPS origins.
|
Package assertion signs short-lived identity handoffs between exact HTTPS origins. |
|
Package auth is the framework's turnkey sign-in and the family default: a magic-link email that works for every address, auto-upgrading to the keymail ceremony when the address resolves to a claimed keymail inbox.
|
Package auth is the framework's turnkey sign-in and the family default: a magic-link email that works for every address, auto-upgrading to the keymail ceremony when the address resolves to a claimed keymail inbox. |
|
Package blobs stores content-addressed bytes — design doc §5's blob layer, built on what the platform actually shipped: rows hold metadata (a Ref: hash, size, content type) while bytes live in a Store, keyed by their SHA-256.
|
Package blobs stores content-addressed bytes — design doc §5's blob layer, built on what the platform actually shipped: rows hold metadata (a Ref: hash, size, content type) while bytes live in a Store, keyed by their SHA-256. |
|
Package carlos is the app side of the CARLOS platform's scheduled-work contract: receiving a tick, and registering a one-shot timer.
|
Package carlos is the app side of the CARLOS platform's scheduled-work contract: receiving a tick, and registering a one-shot timer. |
|
cmd
|
|
|
dsgen
command
Command dsgen writes rastrillo's design-system gallery — every partial, every class idiom, every token, in three themes and twelve languages — to a directory of static files.
|
Command dsgen writes rastrillo's design-system gallery — every partial, every class idiom, every token, in three themes and twelve languages — to a directory of static files. |
|
rastrillo
command
Command rastrillo is the CARLOS web framework's CLI: rastrillo new scaffolds an app, rastrillo generate runs the filesystem-routing generator, rastrillo dev runs the watch/rebuild/restart loop.
|
Command rastrillo is the CARLOS web framework's CLI: rastrillo new scaffolds an app, rastrillo generate runs the filesystem-routing generator, rastrillo dev runs the watch/rebuild/restart loop. |
|
Package crypto is the family envelope (design doc §6): ECDH P-256 ephemeral → HKDF-SHA256 → AES-256-GCM asymmetric sealing, ECDSA P-256 signing with raw r‖s signatures, and the symmetric half (Derive, SealSym, OpenSym) — every operation domain-separated by a caller-supplied context string.
|
Package crypto is the family envelope (design doc §6): ECDH P-256 ephemeral → HKDF-SHA256 → AES-256-GCM asymmetric sealing, ECDSA P-256 signing with raw r‖s signatures, and the symmetric half (Derive, SealSym, OpenSym) — every operation domain-separated by a caller-supplied context string. |
|
Package db opens the application's SQLite database the way a CARLOS app needs it, exposed as one *gorm.DB.
|
Package db opens the application's SQLite database the way a CARLOS app needs it, exposed as one *gorm.DB. |
|
Package eventlog is the rastrillo.Mergeable store shape (design doc §5) — the Eleven shape, extracted fresh since no app had extracted it: a command never UPDATEs; it appends an immutable event to a resource's stream, and a pure Derive fold recomputes the read model.
|
Package eventlog is the rastrillo.Mergeable store shape (design doc §5) — the Eleven shape, extracted fresh since no app had extracted it: a command never UPDATEs; it appends an immutable event to a resource's stream, and a pure Derive fold recomputes the read model. |
|
Package flash provides one-shot notice messages via HTTP cookies.
|
Package flash provides one-shot notice messages via HTTP cookies. |
|
Package form holds the plain, framework-independent helpers a generated form handler needs: money parsing/formatting and a field error map.
|
Package form holds the plain, framework-independent helpers a generated form handler needs: money parsing/formatting and a field error map. |
|
Package gormlite is a GORM SQLite dialector over modernc.org/sqlite.
|
Package gormlite is a GORM SQLite dialector over modernc.org/sqlite. |
|
Package harness drives a rastrillo app in a real Chromium with a CDP virtual authenticator attached — the browser rig behind `go test -tags browser ./...`.
|
Package harness drives a rastrillo app in a real Chromium with a CDP virtual authenticator attached — the browser rig behind `go test -tags browser ./...`. |
|
internal
|
|
|
catalog
Package catalog decodes the flat `key = "string"` TOML subset rastrillo's locale catalogs use (design doc §10: "One TOML file per locale ...
|
Package catalog decodes the flat `key = "string"` TOML subset rastrillo's locale catalogs use (design doc §10: "One TOML file per locale ... |
|
designsystem
Package designsystem renders rastrillo.org/design-system: one static page per theme × locale showing every partial, every markup idiom and every design token the framework ships, plus a full-page demo of each of the four shells and one of the modal route.
|
Package designsystem renders rastrillo.org/design-system: one static page per theme × locale showing every partial, every markup idiom and every design token the framework ships, plus a full-page demo of each of the four shells and one of the modal route. |
|
devloop
Package devloop implements the polling file watcher behind `rastrillo dev` (design doc §11).
|
Package devloop implements the polling file watcher behind `rastrillo dev` (design doc §11). |
|
docsite
Package docsite loads the documentation corpus under docs/site and exposes it in the shape the gates need.
|
Package docsite loads the documentation corpus under docs/site and exposes it in the shape the gates need. |
|
generate
Package generate's action emitter (this file) turns a validated rastrillo.Resource into the (up to) nine action files a manifest owns: gen/actions/<route>/{index.GET,index.POST,new.GET}.go and gen/actions/<route>/[id]/{index.GET,edit.GET,edit-basics.POST, delete.GET,delete.POST}.go, plus [id]/edit-advanced.POST.go when the resource declares Form.Advanced.
|
Package generate's action emitter (this file) turns a validated rastrillo.Resource into the (up to) nine action files a manifest owns: gen/actions/<route>/{index.GET,index.POST,new.GET}.go and gen/actions/<route>/[id]/{index.GET,edit.GET,edit-basics.POST, delete.GET,delete.POST}.go, plus [id]/edit-advanced.POST.go when the resource declares Form.Advanced. |
|
iconsets
Package iconsets holds the vendored data for every icon set and delivery mode rastrillo can scaffold, and renders the app-owned internal/icons/icons.go from it.
|
Package iconsets holds the vendored data for every icon set and delivery mode rastrillo can scaffold, and renders the app-owned internal/icons/icons.go from it. |
|
manifest
Package manifest owns manifest discovery and the JSON artifact (gen/manifest.json) the generator consumes (design doc §3).
|
Package manifest owns manifest discovery and the JSON artifact (gen/manifest.json) the generator consumes (design doc §3). |
|
markup
Package markup is the class→attribute codemod for the ratified markup grammar (design spec §6-v3), and the one place that grammar is written down as code.
|
Package markup is the class→attribute codemod for the ratified markup grammar (design spec §6-v3), and the one place that grammar is written down as code. |
|
Package jobs is the observable handle for background work: Start runs a function in a goroutine and hands back an ID a status page can poll with Get.
|
Package jobs is the observable handle for background work: Start runs a function in a goroutine and hands back an ID a status page can poll with Get. |
|
Package keyring owns the E2EE seed lifecycle the crypto package leaves to apps: one 32-byte seed per person, HKDF purpose derivation namespaced by Ring, the seed wrapped under a passkey's PRF output, content keys granted to members' box keys, and the wraps guard that keeps the last wrap unrevokable.
|
Package keyring owns the E2EE seed lifecycle the crypto package leaves to apps: one 32-byte seed per person, HKDF purpose derivation namespaced by Ring, the seed wrapped under a passkey's PRF output, content keys granted to members' box keys, and the wraps guard that keeps the last wrap unrevokable. |
|
Package mail is the framework's one outbound-email surface — the third extraction of a shape vitogo (internal/vito/mail), kass (internal/mail) and seapointish (smtpMailer) each hand-rolled: a one-method Sender interface, a stdlib net/smtp implementation, a loudly-labelled log fallback for instances with no relay configured, and the header-injection guard all three carried.
|
Package mail is the framework's one outbound-email surface — the third extraction of a shape vitogo (internal/vito/mail), kass (internal/mail) and seapointish (smtpMailer) each hand-rolled: a one-method Sender interface, a stdlib net/smtp implementation, a loudly-labelled log fallback for instances with no relay configured, and the header-injection guard all three carried. |
|
Package migrate applies an app's schema exactly once per migration and records what it did, replacing the two mechanisms — GORM AutoMigrate for models, raw Migrations []string for framework subsystems — that a Rastrillo app used to run side by side at boot.
|
Package migrate applies an app's schema exactly once per migration and records what it did, replacing the two mechanisms — GORM AutoMigrate for models, raw Migrations []string for framework subsystems — that a Rastrillo app used to run side by side at boot. |
|
dump
Package dump is the bridge between the rastrillo binary and an app's model structs.
|
Package dump is the bridge between the rastrillo binary and an app's model structs. |
|
money
module
|
|
|
Package passkey hardens an app's sessions with a WebAuthn second factor on the step-up seam: a signed-in user enrolls a passkey, and a valid-but-stale session (refused by sessions.RequireFresh) is made fresh again by an assertion ceremony instead of a full re-sign-in.
|
Package passkey hardens an app's sessions with a WebAuthn second factor on the step-up seam: a signed-in user enrolls a passkey, and a valid-but-stale session (refused by sessions.RequireFresh) is made fresh again by an assertion ceremony instead of a full re-sign-in. |
|
Package password is an email+password identity plugin on the sessions core: it verifies a submitted credential and calls sessions.SignIn — the same one-call contract auth's keymail flow honors — while leaving user storage, page rendering, and CSRF to the app (csrf.Protect is mounted app-wide, not this package's job).
|
Package password is an email+password identity plugin on the sessions core: it verifies a submitted credential and calls sessions.SignIn — the same one-call contract auth's keymail flow honors — while leaving user storage, page rendering, and CSRF to the app (csrf.Protect is mounted app-wide, not this package's job). |
|
Package pow is the front door for a form anyone on the internet can post to: an address-bound proof of work, a sealed challenge, a single-use nonce and a honeypot, with the browser half of the proof of work shipped alongside the Go half that verifies it.
|
Package pow is the front door for a form anyone on the internet can post to: an address-bound proof of work, a sealed challenge, a single-use nonce and a honeypot, with the browser half of the proof of work shipped alongside the Go half that verifies it. |
|
Package scope makes the right query the short query: every model owned by a user (or team) is read through its owner filter, and a row that isn't yours is a row that doesn't exist — handlers answer 404, never 403 (matching view.ParseID's rule: a URL that was never yours was never a URL).
|
Package scope makes the right query the short query: every model owned by a user (or team) is read through its owner filter, and a row that isn't yours is a row that doesn't exist — handlers answer 404, never 403 (matching view.ParseID's rule: a URL that was never yours was never a URL). |
|
Package sessions maintains signed-in sessions: SQLite-backed rows (so sign-out and admin revocation are real — a deleted row is dead even if the cookie lives on), __Host- cookies on https origins, and the request-context surface (Current, UserID) the rest of an app reads.
|
Package sessions maintains signed-in sessions: SQLite-backed rows (so sign-out and admin revocation are real — a deleted row is dead even if the cookie lives on), __Host- cookies on https origins, and the request-context surface (Current, UserID) the rest of an app reads. |
|
Package tools is the runtime half of the agents system (design doc §8): it renders the generated registry (gen.Tools()) as LLM tool schemas, and dispatches a model-proposed call back through the app's own mux — "a tool call and an HTTP POST reach the identical Handle function" — with every call re-validated against the registry before it executes, the caller attributed on Ctx.Actor, and §8's consent gate enforced: a write tool refuses to run unconfirmed.
|
Package tools is the runtime half of the agents system (design doc §8): it renders the generated registry (gen.Tools()) as LLM tool schemas, and dispatches a model-proposed call back through the app's own mux — "a tool call and an HTTP POST reach the identical Handle function" — with every call re-validated against the registry before it executes, the caller attributed on Ctx.Actor, and §8's consent gate enforced: a write tool refuses to run unconfirmed. |
|
Package ui is rastrillo's server-shape component library: a small starter set of List-screen html/template partials, a design-token stylesheet, and the template helpers they need — vendored the same way icons.go vendors Lucide, so an app pulls in a working component with an import and a ParseFS call, not a hand-copy.
|
Package ui is rastrillo's server-shape component library: a small starter set of List-screen html/template partials, a design-token stylesheet, and the template helpers they need — vendored the same way icons.go vendors Lucide, so an app pulls in a working component with an import and a ParseFS call, not a hand-copy. |
|
Package vault is rastrillo's client half of the Pegamento vault facet: one person's named sealed blobs and per-method wrapped seed on a home service the app's operator may not run.
|
Package vault is rastrillo's client half of the Pegamento vault facet: one person's named sealed blobs and per-method wrapped seed on a home service the app's operator may not run. |
|
Package vectors emits the golden vectors that pin an app's JS derivation engine to its Go one.
|
Package vectors emits the golden vectors that pin an app's JS derivation engine to its Go one. |
|
Package view holds the plain HTTP-response helpers a generated action needs against a *rastrillo.Ctx: rendering a page, failing loudly but safely, and reading the {id} path value.
|
Package view holds the plain HTTP-response helpers a generated action needs against a *rastrillo.Ctx: rendering a page, failing loudly but safely, and reading the {id} path value. |
|
Package webauthn verifies passkey registrations and assertions — the extraction design doc §7 names: kass and slopbox carried duplicate copies of this package (kass's internal/webauthn is the source lifted here, tests and all), and each thing it leaves out is a thing that cannot be got wrong.
|
Package webauthn verifies passkey registrations and assertions — the extraction design doc §7 names: kass and slopbox carried duplicate copies of this package (kass's internal/webauthn is the source lifted here, tests and all), and each thing it leaves out is a thing that cannot be got wrong. |
|
authtest
Package authtest is a passkey authenticator for tests.
|
Package authtest is a passkey authenticator for tests. |