๐ค The CLI
One binary, eleven commands. Install it with:
go install amadan.net/rastrillo/rastrillo/cmd/rastrillo@latest
Every command except session-key takes an optional directory as its last argument and defaults to .. session-key takes a key id instead. Flags come before the directory. rastrillo help, -h and --help all print usage.
rastrillo new
rastrillo new [--icons=<set>] [--icon-delivery=<mode>] [--ux=<profile>]
[--theme=<name>] [--shell=<name>] [--locale=<code>]
[--auth=<kind>] <name>
Scaffolds a complete app in ./<name> โ one that compiles, passes its
own tests, and serves, before you have written anything. It runs
generate once on the way out so go build works immediately. Then it
runs go mod tidy in the app and compiles its templates, so the app
starts with its rastrillo_templates_gen.go. If that cannot run, for
example offline, new still succeeds and prints a line telling you to
run rastrillo generate after go mod tidy.
| Flag | Values | Default |
|---|---|---|
--icons |
lucide, font-awesome |
lucide |
--icon-delivery |
inline, cdn, js |
inline |
--ux |
considered, standard |
considered |
--theme |
day, plain, signal |
day |
--shell |
column, topbar, sidebar, console |
column |
--auth |
none, magic |
none |
--locale |
en, ga, zh-Hans, es, hi, pt-PT, bn, ru, ja, yue, vi, ar |
en |
All six set ร delivery combinations scaffold, compile and pass
generate --check. The icon set becomes an ordinary app-owned package
under internal/<app>/icons, and --ux seeds a UX convention profile
into AGENTS.md, which is the source of truth from then on โ nothing
re-reads the profile name afterwards. Icons explains what
each delivery mode costs, including the one worth repeating: with js,
icons do not render at all without JavaScript.
--theme picks the colour, type and shape stylesheet written as
static/theme.css, and --shell picks the page frame written as
templates/layout.html. Both are copied in verbatim and are yours from
that moment โ the theme like tokens.css, the layout like every other
template. Every value is checked before a single file is created, so a
typo fails with your working directory still clean.
Templates describes the three of each.
--icons=font-awesome also writes the CC BY 4.0 attribution the licence
requires, because that obligation is the app's and has to travel with
the code.
--auth=magic starts the app with sign-in by emailed link: the magic links plugin with its sign-in screen, a Sign in and Sign out control in the header, and an example page only a signed-in person can open. Rows belong to the signed-in address, read and written through the owned seam in auth.go. Until you set <APP>_SMTP_HOST and <APP>_SMTP_FROM, each link is written to the log instead of sent. The app's own tests sign in through the emailed link, so make ci passes from the first commit.
--locale picks the one language the app starts with, written to
manifest/app.toml. It must be one the framework's own strings ship in,
and pt means pt-PT. Anything else fails before a file is written:
rastrillo new --locale "fr": the framework's own strings ship in en, ga, zh-Hans, es, hi, pt-PT, bn, ru, ja, yue, vi, ar. Start with one of those, then add your language to manifest/app.toml
The app offers that one language, with no /xx/ prefixes and no
switcher. Add more later; see Localization.
rastrillo generate
rastrillo generate [--check] [--accept-breaking] [--templates=false] [dir]
Walks actions/ and manifest/ and emits gen/: the router on a Go
1.22 http.ServeMux, every declared resource's store, screens and
locale keys, gen/boot.go, and gen/locales/packs.go, which links the
formatting packs for the languages in manifest/app.toml. Fails loudly
on route collisions.
It removes any file in gen/ that an earlier run wrote and nothing
writes now, such as the screens of a resource you deleted, and prints a
line for each:
removed gen/templates/ticket_types/confirm.html: generated by an earlier run, and nothing generates it now.
--check verifies without writing, and is the pre-ship gate. It checks
route collisions, action build tags, icon slugs that nothing answers,
and i18n catalog completeness โ and, when the app has a
cmd/genvectors, it runs the parity-vectors gate too, so
vectors --check never needs a separate CI step. Only --check fails on an incomplete
catalog โ plain generate, and so dev and new, never does. That
split is deliberate: silent fallback while you iterate, loud failure
before you ship.
--check compares every catalog with the one for default_locale in
manifest/app.toml. The --default-locale flag was removed, and
passing it fails with
--default-locale was removed: the default comes from default_locale in manifest/app.toml.
generate also writes api/v1.json at the app root: every route
/api/v1 serves, with its arguments and result fields. Commit it.
Before writing, generate compares the file it would write with the
committed one. A change that only adds (a new route, a new optional
argument, a new result field) is written. A change that breaks a client
(a route or field removed, an argument newly required, a kind changed)
fails, names each break, and leaves the file as it was. To rewrite it
over a break on purpose, run rastrillo generate --accept-breaking; the
diff shows the break in review. --check fails if the file is missing
or stale, and never writes it.
Last, generate compiles your templates to Go, manifests or not. It
starts each program under cmd/ whose func main calls rastrillo.Run
or rastrillo.Serve, records the template sets it builds, and writes
rastrillo_templates_gen.go into each package that builds one. Commit
those files. Anything your program does before that call still runs during generate. Nothing after it does: capture mode exits the process after rastrillo.Serve writes the capture. generate never starts any
other program, so a migration or a mail-out under cmd/ does not run.
A template it cannot compile, a
program that cannot start this way, and a program it does not start get
one line each, and those templates render through html/template as
before. See tmpl. Pass --templates=false to
skip this step.
--check fails when a rastrillo_templates_gen.go is missing or out of
date with your templates. It also fails when a program cannot start
this way and imports a package with a rastrillo_templates_gen.go,
because it cannot check that file. Fix what stops the program, or run
--check where it can start.
rastrillo dev
rastrillo dev [dir] [-- app args]
The watch loop: polls app/, actions/, manifest/, cmd/,
locales/ and templates/, and on any change reruns generate,
rebuilds ./cmd/<name> to a temporary binary, and restarts the process
with a graceful SIGTERM. Anything after -- is passed to your app.
dev leaves compiled templates out. It runs
generate --templates=false and builds with -tags rastrillo_dev, so
every page renders through html/template from the files on disk, and
a compiled file that no longer fits your code cannot break the build.
Run rastrillo generate before you commit: generate --check fails
until you do.
A failed generate or rebuild keeps the previous build serving, and a
failed restart keeps the loop watching โ either way the next save
retries. It expects the rastrillo new layout: exactly one directory
under cmd/.
rastrillo migration
Schema changes. The group is a noun on purpose: this CLI never applies
migrations, because migrations run at boot โ a hibernating route has no
operator moment between a new binary landing and the activator exec'ing
it. baseline is the one exception, and it is manual by design.
Migrations is the guide; these are the commands.
migration generate
rastrillo migration generate [--allow-destructive] [dir]
Diffs your models against your migrations and writes the delta as a new
numbered migration. Prints nothing to do when they already agree.
A change that drops data is refused unless you pass
--allow-destructive, and the refusal prints the SQL it would have
written so you can see exactly what it thinks is destructive.
Read the generated SQL before committing it. generate may emit a full
table rebuild rather than an ALTER, and a rename is indistinguishable
from a drop plus an add to any tool โ write those by hand with
migration new.
migration check
rastrillo migration check [dir]
The CI gate: exits non-zero when models and migrations disagree, listing
the SQL that would close the gap. Touches no database, so it runs
anywhere. make ci in a scaffolded app runs it for you.
migration new
rastrillo migration new <name> [dir]
Writes a numbered stub migration for a change you will write by hand โ a rename, a data backfill, anything the differ cannot infer. The name must be lowercase letters, digits and underscores.
migration status
rastrillo migration status --db <path> [dir]
What a real database's ledger has applied, plus any pending drift.
Requires --db because it reports on a database rather than on source.
A database that has never booted this version gets a plain "no ledger
yet" line rather than an error.
migration baseline
rastrillo migration baseline --db <path> [--through <id>] [dir]
Stamps a ledger by hand, for the case where boot refuses to adopt an existing database. It runs no DDL โ it only records migrations as applied.
Order matters, and getting it wrong loses a migration. Run
baseline --through <id> first, stamping only up to the migration the
database already matches, and then apply the missing one by hand; the
next boot runs the rest. Bare baseline, with no --through, stamps
everything: while the ledger is still empty any boot finds a matching
schema and adopts it, recording every later migration as applied without
running it โ which silently skips a pending data migration. That is
what it is for, and it is why it is manual.
Migrations walks the whole recovery.
rastrillo doctor
rastrillo doctor [--fix] [--force] [--theme <name>] [--probe] [dir]
Compares the files rastrillo new copied into the app's static/
directory (tokens.css, theme.css, rastrillo.js, select.js,
datetime.js and light.js) against the copies this binary carries,
and says which ones differ and how.
It is a convenience and an upgrade tool, not the thing standing between
your app and silent drift. That is the vendored_test.go the scaffold
writes, which runs in CI on every commit without anyone remembering to.
What doctor adds is re-copying rather than telling you to, working on
an app that never had that test, saying how a file differs rather than
that it does, and running from outside the app โ which is what asking
"is this one safe to upgrade?" about somebody else's repository needs.
| Flag | Purpose |
|---|---|
--fix |
Re-copy each drifted file from this binary's library copy |
--force |
With --fix: re-copy across a version mismatch, and over files recorded as deliberate edits or removals |
--theme |
Which theme static/theme.css should be, for an app with no pin to read it from |
--probe |
Also build the app and run it to check its language setup. This runs your app's own code |
The version it compares against
The CLI carries its own compiled-in copy of the library; your app has
its own required version in go.mod. These are frequently different,
and that difference is not drift โ an app deliberately on v0.19.0
checked by a v0.20.0 binary has files that correctly match v0.19.0.
So doctor reads both, says which one it compared against, and makes a
mismatch the first line of the report rather than a footnote:
rastrillo doctor is v0.20.0; this app requires v0.19.0.
Comparing against v0.20.0 โ upgrade the module first, or these differences are expected.
--fix refuses in that state. Copying v0.20.0 assets into an app that
compiles against v0.19.0 produces exactly the fault this checks for โ
new CSS against old markup โ with the difference that doctor would
then call it clean. Upgrade the module first, or pass --force.
An app with a replace directive is not a mismatch: it builds against a
checkout, so there is no second version to disagree with. doctor says
which checkout and compares against its own copy, which is right only if
this binary was built from that tree.
What it will not call drift
A hand-edited theme is a supported thing, not damage. If
static/theme.css matches no shipped theme, doctor says custom or
drifted and compares nothing โ it will not pick the closest theme and
report a diff against a guess. Pass --theme <name> when you do want it
compared against a shipped one.
A file you edited on purpose is the same: name it in vendoredIsMine in
the scaffold's vendored_test.go and both that test and doctor leave
it alone. Apps scaffolded before that map existed recorded the same
thing by deleting the file's line from the pin, and doctor reads that
too โ but it reads such a file rather than assuming. The first version
of that pin listed three files, and theme.css and datetime.js joined
the vendored set afterwards, so a name an old pin never mentions may
simply mean "this app predates it". You delete a pin line to protect an
edit, so the edit is the evidence: a file that is missing is absent
and --fix delivers it, a file identical to the library is checked
normally, and only a file that is there and differs is left alone as
yours. That last rule is also what stops --fix from installing a file
and thereby exempting it from every check afterwards.
A file you deleted is the same again. Dropping select.js from an app
with no big selects is a supported choice, so an absent file is reported
as absent, not as drift, and does not fail the exit code โ you get a
line saying what the library ships and how big it is. --fix will still
add it, because asking for --fix is asking.
An app on the old module path
Until v0.25.0 rastrillo's module path was
github.com/carlosframework/rastrillo. An app that still imports from
there is stuck at v0.23.0. It gets no fix released since, and its own
vendored_test.go still passes, because it checks against the same old
module. go get github.com/carlosframework/rastrillo/cmd/rastrillo@upgrade fails with
"module declares its path as: amadan.net/rastrillo/rastrillo".
doctor checks for this first. If the app's go.mod requires the old
path directly, it compares no files, prints the steps below and exits 5.
--fix refuses, even with --force.
The app's own CLI is on the old path too, so run a current one:
go run amadan.net/rastrillo/rastrillo/cmd/rastrillo@latest doctor
To move the app, run these from its root on a clean tree:
git grep -lz github.com/carlosframework/rastrillo -- ':(glob,exclude)**/go.mod' ':(glob,exclude)**/go.sum' |
xargs -0 perl -pi -e 's#github\.com/carlosframework/rastrillo#amadan.net/rastrillo/rastrillo#g'
go mod edit -droprequire=github.com/carlosframework/rastrillo \
-droptool=github.com/carlosframework/rastrillo/cmd/rastrillo \
-tool=amadan.net/rastrillo/rastrillo/cmd/rastrillo
go get amadan.net/rastrillo/rastrillo@latest
go mod tidy
go tool rastrillo doctor --fix
The text rewrite leaves go.mod alone on purpose. Many apps already
require the new path, and rewriting would add a second requirement on it
at v0.23.0, a version that does not build under the new path. Then read
the changelog for each release since the one you were on: some change
markup your app keeps a copy of.
If other modules sit inside the app, doctor names the ones on the old
path. The text rewrite changes their imports too, so run the go mod
steps in each of them.
Templates and locales
After the static files, doctor checks every app for two things this
release needs, under the headings Templates: and Locales:. --fix
skips both.
Templates: runs rastrillo migrate-templates on the app without
writing: every template still in the old syntax, and every Go file that
registers its own T. Exit code 6 means there is work left. See
Upgrading templates.
Locales: checks that gen/locales/packs.go matches
manifest/app.toml, and that each language you offer has the catalog
it needs. It does not run your app. Exit code 7 means the app would
refuse to start.
To also check the language setup by running the app, add --probe.
doctor then builds the app's command, cmd/<app>, and runs it with
RASTRILLO_CHECK_CONFIG=1. The app checks its language setup, prints
what it found and exits. This runs your app's own code first: anything
its main or an init function does before it reaches rastrillo still
happens. Use --probe where that does no harm, such as a checkout with
no live database. An app on a rastrillo from before the probe is not
run, and doctor says so.
Exit codes
| Code | Meaning |
|---|---|
0 |
Every compared file matches |
1 |
An error โ not an app, unreadable files |
2 |
Usage |
3 |
Drift: files differ from the library copy |
4 |
The app and the CLI are on different rastrillo versions, so the comparison is not authoritative |
5 |
The app imports rastrillo from its old module path; nothing was compared |
6 |
templates still use the old syntax: run rastrillo migrate-templates |
7 |
the app's locale configuration would stop it starting |
Drift and version mismatch are separate codes because they call for opposite actions: one means "re-copy these", the other means "do not re-copy anything yet".
When more than one applies, doctor exits with the first of 5, 4, 3, 6
and 7. It prints every section either way.
rastrillo markup
rastrillo markup [--fix] [dir]
Rewrites an app's markup from the class spelling of the UI vocabulary to
the attribute spelling: <div class="rst-box"> becomes <div rst-box>,
class="rst-callout__body" becomes rst-callout-body, class="rst-btn rst-btn--primary" becomes rst-btn="primary", and data-tone becomes
rst-tone. Seven utility classes stay in class, because that is what
class is for: rst-sr-only, rst-mono, rst-m-hide, rst-grow,
rst-nm, rst-danger and rst-cell-mut.
It reads templates (.html, .htm, .gohtml, .tmpl), Go source
(.go โ markup in a string literal or a doc comment), JavaScript
(.js) and CSS (.css), and it is the same tool the framework flipped
itself with. It skips .git, node_modules, vendor,
.design-system and .superpowers.
It does not read Markdown
.md files are not scanned, deliberately. A Markdown file has no
markup to migrate; it has discussion of markup โ an example in a code
span, a migration table, a sentence naming both spellings so a reader
can tell them apart. Rewriting that destroys it, and the damage does
not look like damage: "class="rst-box" and <div rst-box> are
identical" becomes a sentence claiming two identical-looking things are
identical, and the diff reads fine.
The costs point one way. An example left in the old spelling is stale, visible and harmless โ you fix it when you next read the page. A rewritten explanation is gone, and the pages most likely to contain the string are the ones teaching the difference between the two spellings, so the tool would erase its own rationale first.
So the documentation is yours to update. Grep for class="rst- in your
.md files after a migration and decide, page by page, which examples
should now teach the attribute spelling and which are describing the
history on purpose.
v0.22.0 did scan .md, and this is a change from it. On that
release, rastrillo markup over a repository of pure documentation
could report files to rewrite and exit 3 with no template anywhere in
it. The count is a count of files with markup in them again โ which is
what makes it the number to size the migration by.
| Flag | Purpose |
|---|---|
--fix |
Write the rewrite. Without it, the command reports what would change and writes nothing |
| Exit | Meaning |
|---|---|
0 |
Nothing to do, or --fix finished with nothing left over |
2 |
Usage |
3 |
There is work here: a rewrite waiting for --fix, or a class attribute only you can take apart |
Do the upgrade in one sitting, in this order:
rastrillo doctor --fix # 1. re-copy tokens.css
rastrillo markup # 2. read what would change
rastrillo markup --fix # 3. write it
go test ./...
Step 1 is not optional. tokens.css is copied into your static/ at
scaffold time and frozen there while the partials it styles keep
upgrading, so an app on the old, class-only stylesheet whose partials
have started emitting attributes renders unstyled. That is the one
failure this staged migration exists to avoid.
Rewriting is idempotent, so running it twice, or over a tree half of
which is already done, changes nothing the second time. Nothing breaks
if you stop between steps 1 and 3, either: tokens.css styles both
spellings until stage 3, and rastrillo.js dismisses menus written in
either one. Step 3 is what you owe stage 3, not what you owe today.
One name changed meaning
rst-form-foot used to be the sticky save bar you wrote by hand, and
rst-form__foot the plain closing row the form-foot partial emits.
BEM's __ flattens to a hyphen, so both wanted the same attribute, and
one attribute cannot carry two rules. The partial's row took the name
the partial is called:
| Was | Is |
|---|---|
class="rst-form__foot" โ the partial's closing row |
rst-form-foot |
class="rst-form-foot" โ your sticky save bar |
rst-form-bar |
class="rst-form-foot__note" |
rst-form-bar-note |
This is the breaking change in the release. If you hand-wrote a save
bar and do not run the codemod, your class="rst-form-foot" now means
the plain row: no border, no background, and it no longer sticks.
rastrillo markup --fix applies the rename, and prints a reminder on
every run that changes anything.
The vendored files
It never touches static/tokens.css, static/theme.css,
static/rastrillo.js, static/select.js, static/datetime.js or
static/light.js. Those
are copies of the library's, and doctor is what refreshes them;
rewriting one here would make your copy differ from the library's for
good.
The one opt-out
A line carrying markup-spelling: old-spelling begin starts a region
the tool will not rewrite; markup-spelling: old-spelling end closes
it, and an unclosed one runs to the end of the file. Use it in a file
that is scanned but whose subject is the spelling you are migrating
away from: the Go doc comment holding a before/after table, the HTML
page of your own docs that shows both spellings side by side. Markdown
needs no fence, because Markdown is not read at all.
What it reports instead of guessing
It leaves alone โ and prints โ any class attribute whose shape it cannot read: markup built by concatenating string literals, a class list a template assembles in a way it cannot take apart, or markup written as escaped text in a page of your own documentation. A wrong guess renders unstyled and looks like markup somebody wrote on purpose. Those keep the exit code at 3 until you have dealt with them, so a CI gate cannot mark a half-migrated app done.
Your own stylesheet
The report ends with the .rst- selectors in CSS you own โ every
stylesheet except a byte-identical copy of the library's, and every
<style> block in a template. Utility classes are left out; they are
still classes and your rules still match them.
A rule you wrote against .rst-lrow stops matching the moment your
markup says rst-lrow, and no test in your app will notice. Change them
to attribute selectors โ .rst-lrow becomes [rst-lrow] โ which weigh
exactly the same, so nothing in your cascade moves.
rastrillo migrate-templates
rastrillo migrate-templates [-write] PATH... move templates to the localizer-first syntax; exits 1 while anything is left to do
Adds the page's localizer, $L, to every template under the paths you
give it. Without -write it changes nothing and reports what it would
do. Upgrading templates has the whole
procedure.
rastrillo vectors
GoโJS parity vectors: the app's cmd/genvectors enumerates golden cases
from the Go engine, this verb writes them to test/vectors.json, and
the app's JS suite must reproduce every one. The derivation engine an
app runs client-side exists twice by necessity, and two engines drifting
is the E2EE bug class where a wrong answer looks fine.
rastrillo vectors [--init] [--check] [dir]
Plain vectors runs the app's own generator and writes its stdout to
test/vectors.json โ a new root-level directory, chosen because the JS
suite is neither a Go package nor a static asset.
| Flag | Purpose |
|---|---|
--init |
Scaffold cmd/genvectors, the test/ parity suite, and the go-test belt into an existing app (once) |
--check |
Pre-ship gate: regenerate, byte-compare, then run the JS parity suite with node --test test/parity.test.mjs |
generate --check runs this same gate automatically when
cmd/genvectors exists โ one gate before ship, not two to remember; CI
that already runs generate --check needs no extra step.
rastrillo budget
rastrillo budget size [dir]
rastrillo budget code [--init | --lower] [dir]
rastrillo budget test [-no-time] [-require TestA,TestB] [go test arguments]
budget size counts the lines in each directory of the module at dir
(default .) and holds them to 5,000 lines of code and 8,000 of tests,
minus the exceptions in .rastrillo/budgets.txt. It leaves out generated
files, the ones whose first lines say // Code generated ... DO NOT EDIT..
It stops at a nested module, which has its own budget. It exits 1 when something is over and 2
when budgets.txt cannot be read.
budget code holds the Go code of the module at dir (default .) to
four limits. A line is at most 80 columns, counting a tab as up to 4. A
file is at most 400 lines, and a function at most 40, or 15 for an action.
No name is a single letter, except t, b and f with their testing
types, and a one-capital-letter type parameter. Tests are held to the
columns and names limits only. Generated files are skipped, and a file
marked //rastrillo:data <reason> above its package clause is a table, so
it is not held to columns or length.
A file marked //rastrillo:vendored <source>, a copy of code from
elsewhere, is skipped like a generated one.
.rastrillo/code.txt records the offences the code already had. A new
offence fails, and so does a record that is looser than the code: lower
it, or delete it when the offence is gone. --init writes the file with a
record for every offence, and refuses if the file exists. --lower lowers
and deletes records to match the code, and never adds or raises one. To
add or raise a record by hand, write a reason after its count. The check
compares the file with the copy where your branch left origin/main, and
fails a new or higher record with no reason. It exits 1 when something is
over a limit and 2 when code.txt cannot be read or --init finds one
already there.
budget test runs go test -json with your arguments (./... if you give
none), and exits with go test's own status. It holds each package to its
time budget using the line budget.Main prints, fails a package that ran
tests without printing it, and ends with a receipt. -no-time turns the
time budget off, for the perf lane. -require names tests that must pass
in the run, so a lane cannot go green having run nothing.
The scaffold's make budget, make test and make perf call budget size
and budget test.
Testing explains the budgets.
rastrillo routes
rastrillo routes [--package path] [--check] [dir]
Prints the routes of the app at dir (default .), one a line, in the
order they are registered: the method, the full path, the action and the
guards. The last line is a reminder of what guards do not decide.
GET / app.(*App).index
GET /tags app.(*App).tagList
POST /tags/{id} app.(*App).tagRename signedIn
GET /admin app.(*App).adminHome admin
* /api/* api
Guards are page middleware. An operation's Authorize can still refuse a request that passes them.
It does not start the app. It calls the app's Routes function with an
empty App and a table that only records. Write the function in a
package under internal/:
func Routes(routes *action.Routes, app *App)
App can be any exported type in that package, or a pointer to one.
Routes must only register routes: no queries, no network calls,
nothing built. See action.
To call it, routes builds a small program inside your module with the
go command's -overlay, and runs it. Nothing is written into the app,
and go.mod and go.sum stay as they are. What the app prints while it
starts goes to standard error, so the table on standard output stays
clean.
If your module has vendor/modules.txt, routes builds with your
vendored code, as go build does, and needs no network. Otherwise it
uses the module cache and changes neither go.mod nor go.sum.
| Flag | Purpose |
|---|---|
--package |
The import path of the package whose Routes to call, when more than one package has one |
--check |
Also fail for each action no route mounts |
--check finds every action in the module: each function or method that
takes one *action.Visit and returns nothing. It names each one that no
route mounts. Mount it in Routes, or delete it. An action mounted
through an interface value counts as not mounted: mount the method on
its type instead.
routes refuses to run, and exits 1, in three cases:
- In a Go workspace, because Go may write
go.work.sumthere. Run it withGOWORK=off, or outside the workspace. - On a system other than Linux and macOS, where it cannot promise to stop and clean up what it starts.
- When
Routesis in a package below a secondinternaldirectory, such asinternal/feature/internal/web. The program it builds cannot import that package. MoveRoutesto a package under the top-levelinternal/.
Exits 1 when the routes cannot be listed, or when --check finds an
action no route mounts, and 2 when the arguments are wrong. Ctrl-C stops
it and removes what it built.
rastrillo session-key
rastrillo session-key <kid>
Makes a new signing key for signed sessions and prints it as two lines:
secret <kid>.<private key>
public <kid>.<public key>
Store the value after secret on the hub, with carlos secrets set. The hub signs session cookies with it. Set the value after public on every tenant, with carlos env set. Tenants check cookies with it.
The kid is 1 to 32 characters from a-z, 0-9, _ and -.
To rotate, make a key with a new kid and follow the rotation order.
Exits 2 when the kid is missing or not valid, and prints nothing on standard output.
Read this page as markdown โ exact, unstyled, and cheap for an agent to fetch.