rastrillo / idear Public
The example board
A complete app on rastrillo + idear: a shared message board whose roster is idear's. It is the worked reference ../SKILL.md points at, and it is part of the amadan.net/rastrillo/idear module rather than a submodule — a nested module would need a replace directive pointing at its own parent, which is exactly the thing an app must never write.
Run it
BOARD_SEED=1 go run ./example -addr 127.0.0.1:8080 -db /tmp/board.dbThen sign in at <http://127.0.0.1:8080/signin> as one of the seeded accounts, all with the password demo-password:
| Address | Role | What you can do | | ------------------- | ------ | ------------------------------------------------- | | ada@example.test | Owner | everything, including transferring ownership | | kim@example.test | Admin | invite and manage Members only | | sam@example.test | Member | read the roster; post; nothing else |
Three accounts at three roles, because a role gate you cannot click on is a role gate nobody checks. Sign in as Kim and the role selector offers Member and nothing else; sign in as Sam and the members page offers no controls at all.
BOARD_ORIGIN sets the external origin (it decides the CSRF check and the cookie attributes); BOARD_NAME sets the instance's display name on the public invitation page. Both default loudly.
There is no mail server here, so HandlerConfig.Deliver is nil and idear puts the invitation link itself in the flash notice shown to the admin who minted it. Copy it out of the page. A deployed app sets Deliver and keeps the token out of the browser entirely.
What to read
| File | What it shows | | -------------- | ------------------------------------------------------------------- | | app.go | the whole wiring, in the order it has to happen, with the reasons | | models.go | the app's own User, BootSchema, and a seed through the real flows | | render.go | the two idear render callbacks, and idear.TokenFrom in RenderSignup | | handlers.go | idear.From(r), and an app route gated on Admin | | app_test.go | the whole flow through real HTTP — the thing to copy |
Four things in here are load-bearing rather than stylistic, and each has a comment at the site saying so:
/is behindRequire. Under the password plugin, deactivation is enforced per request byRequire, not at sign-in —password.Signinruns Lookup → Verify → mint with no idear involvement. An ungated landing page is a page a removed member can still read.POST /signupis wrapped inrs.CarryToken. Without it the invitation token never reaches admission and every invited signup is refused.- One 404 renderer, bound once and handed to both
idear.Config.NotFoundand chi's ownNotFound. Two of them is a membership oracle, and it is the one misconfiguration idear cannot detect at runtime. renderSignupseeds its hiddeninvitefield fromidear.TokenFrom(r).password.PageDatahas nowhere to carry a token, so without this a signup that fails validation re-renders a form with an empty field and the invitee's second attempt is refused. If you rewrite this page — the likeliest thing to do to it — keep that line.